From 5edc92c5434c2da7db86867a1d9717ddcb4dd16a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 23 May 2009 00:01:21 +0000 Subject: [PATCH] added XmlBinding git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/XmlEditor@4113 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../XmlBinding/Configuration/AssemblyInfo.cs | 31 + .../Src/CodeCompletion/XmlCompletionData.cs | 105 ++ .../XmlCompletionDataCollection.cs | 289 ++++ .../XmlCompletionDataImageList.cs | 26 + .../XmlCompletionDataProvider.cs | 183 +++ .../Src/Commands/AddAttributeCommand.cs | 26 + .../Src/Commands/AddChildCommentCommand.cs | 27 + .../Src/Commands/AddChildElementCommand.cs | 26 + .../Src/Commands/AddChildTextNodeCommand.cs | 26 + .../Src/Commands/AssignStylesheetCommand.cs | 64 + .../Commands/CodeCompletionPopupCommand.cs | 28 + .../Src/Commands/CreateSchemaCommand.cs | 63 + .../Src/Commands/FormatXmlCommand.cs | 28 + .../Commands/GoToSchemaDefinitionCommand.cs | 29 + .../Src/Commands/InsertCommentAfterCommand.cs | 26 + .../Commands/InsertCommentBeforeCommand.cs | 27 + .../Src/Commands/InsertElementAfterCommand.cs | 26 + .../Commands/InsertElementBeforeCommand.cs | 26 + .../Commands/InsertTextNodeAfterCommand.cs | 27 + .../Commands/InsertTextNodeBeforeCommand.cs | 27 + .../Src/Commands/OpenStylesheetCommand.cs | 34 + .../Src/Commands/RemoveAttributeCommand.cs | 27 + .../RemoveXPathHighlightingCommand.cs | 23 + .../Src/Commands/RunXslTransformCommand.cs | 93 ++ .../Src/Commands/ValidateXmlCommand.cs | 32 + .../XmlBinding/Src/Gui/AddXmlNodeDialog.cs | 341 +++++ .../Src/Gui/GoToSchemaDefinitionEditAction.cs | 25 + .../XmlBinding/Src/Gui/IAddXmlNodeDialog.cs | 31 + .../XmlBinding/Src/Gui/IXmlTreeView.cs | 201 +++ .../XmlBinding/Src/Gui/SelectXmlSchemaForm.cs | 90 ++ .../XmlBinding/Src/Gui/XPathQueryControl.cs | 704 +++++++++ .../XmlBinding/Src/Gui/XPathQueryPad.cs | 68 + .../XmlBinding/Src/Gui/XmlDisplayBinding.cs | 86 ++ .../XmlBinding/Src/Gui/XmlTreeView.cs | 137 ++ .../XmlBinding/Src/Gui/XslOutputView.cs | 38 + .../XmlBinding/Src/Parser/Parser.cs | 69 + .../XmlBinding/Src/Parser/QualifiedName.cs | 127 ++ .../Src/Parser/QualifiedNameCollection.cs | 274 ++++ .../XmlBinding/Src/Parser/XmlElementPath.cs | 148 ++ .../XmlBinding/Src/Parser/XmlParser.cs | 799 ++++++++++ .../XmlBinding/Src/Src/XPathNodeMatch.cs | 167 ++ .../XmlBinding/Src/Src/XPathNodeTextMarker.cs | 60 + .../Src/Src/XmlAttributePropertyDescriptor.cs | 95 ++ .../Src/Src/XmlAttributeTypeDescriptor.cs | 94 ++ .../Src/Src/XmlCharacterDataTreeNode.cs | 60 + .../XmlBinding/Src/Src/XmlCommentTreeNode.cs | 60 + .../Src/Src/XmlEditorAddInOptions.cs | 119 ++ .../Src/Src/XmlEditorOptionsPanel.cs | 49 + .../XmlBinding/Src/Src/XmlElementTreeNode.cs | 99 ++ .../Src/Src/XmlSchemaAssociation.cs | 157 ++ .../Src/XmlSchemaAssociationListBoxItem.cs | 89 ++ .../Src/Src/XmlSchemaCompletionData.cs | 1348 +++++++++++++++++ .../Src/XmlSchemaCompletionDataCollection.cs | 292 ++++ .../Src/Src/XmlSchemaListBoxItem.cs | 59 + .../XmlBinding/Src/Src/XmlSchemaManager.cs | 223 +++ .../XmlBinding/Src/Src/XmlSchemasPanel.cs | 412 +++++ .../XmlBinding/Src/Src/XmlTextTreeNode.cs | 60 + .../XmlBinding/Src/Src/XmlTreeEditor.cs | 654 ++++++++ .../Src/Src/XmlTreeViewContainerControl.cs | 924 +++++++++++ .../XmlBinding/Src/Src/XmlTreeViewControl.cs | 578 +++++++ .../Src/StylesheetAssignedCondition.cs | 33 + .../XmlBinding/Src/Xml/EncodedStringWriter.cs | 48 + .../XmlBinding/Src/Xml/XmlEncoder.cs | 92 ++ .../XmlBinding/Src/Xml/XmlNamespace.cs | 66 + .../XmlBinding/Src/XmlFoldingStrategy.cs | 266 ++++ .../XmlBinding/Src/XmlFormattingStrategy.cs | 189 +++ .../XmlBinding/XmlBinding.addin | 292 ++++ .../XmlBinding/XmlBinding.csproj | 159 ++ 68 files changed, 11201 insertions(+) create mode 100644 src/AddIns/BackendBindings/XmlBinding/Configuration/AssemblyInfo.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionData.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataCollection.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataImageList.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataProvider.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddAttributeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildCommentCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildElementCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildTextNodeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/AssignStylesheetCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/CodeCompletionPopupCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/CreateSchemaCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/FormatXmlCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/GoToSchemaDefinitionCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentAfterCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentBeforeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementAfterCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementBeforeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeAfterCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeBeforeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/OpenStylesheetCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveAttributeCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveXPathHighlightingCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/RunXslTransformCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Commands/ValidateXmlCommand.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/AddXmlNodeDialog.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/GoToSchemaDefinitionEditAction.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/IAddXmlNodeDialog.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/IXmlTreeView.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/SelectXmlSchemaForm.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryControl.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryPad.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlDisplayBinding.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlTreeView.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Gui/XslOutputView.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Parser/Parser.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedName.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedNameCollection.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlElementPath.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlParser.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeMatch.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeTextMarker.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributePropertyDescriptor.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributeTypeDescriptor.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCharacterDataTreeNode.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCommentTreeNode.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorAddInOptions.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorOptionsPanel.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlElementTreeNode.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociation.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociationListBoxItem.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionData.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionDataCollection.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaListBoxItem.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaManager.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemasPanel.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTextTreeNode.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeEditor.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewContainerControl.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewControl.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/StylesheetAssignedCondition.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Xml/EncodedStringWriter.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlEncoder.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlNamespace.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/XmlFoldingStrategy.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/Src/XmlFormattingStrategy.cs create mode 100644 src/AddIns/BackendBindings/XmlBinding/XmlBinding.addin create mode 100644 src/AddIns/BackendBindings/XmlBinding/XmlBinding.csproj diff --git a/src/AddIns/BackendBindings/XmlBinding/Configuration/AssemblyInfo.cs b/src/AddIns/BackendBindings/XmlBinding/Configuration/AssemblyInfo.cs new file mode 100644 index 0000000000..69824e116c --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Configuration/AssemblyInfo.cs @@ -0,0 +1,31 @@ +#region Using directives + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("XmlBinding")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("XmlBinding")] +[assembly: AssemblyCopyright("Copyright 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all the values or you can use the default the Revision and +// Build Numbers by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionData.cs b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionData.cs new file mode 100644 index 0000000000..563c93ce93 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionData.cs @@ -0,0 +1,105 @@ +// +// +// +// +// $Revision: 2932 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ + /* + /// + /// Holds the text for namespace, child element or attribute + /// autocomplete (intellisense). + /// + public class XmlCompletionData : ICompletionData + { + string text; + DataType dataType = DataType.XmlElement; + string description = String.Empty; + + /// + /// The type of text held in this object. + /// + public enum DataType { + XmlElement = 1, + XmlAttribute = 2, + NamespaceUri = 3, + XmlAttributeValue = 4 + } + + public XmlCompletionData(string text) + : this(text, String.Empty, DataType.XmlElement) + { + } + + public XmlCompletionData(string text, string description) + : this(text, description, DataType.XmlElement) + { + } + + public XmlCompletionData(string text, DataType dataType) + : this(text, String.Empty, dataType) + { + } + + public XmlCompletionData(string text, string description, DataType dataType) + { + this.text = text; + this.description = description; + this.dataType = dataType; + } + + public int ImageIndex { + get { + return 0; + } + } + + public string Text { + get { + return text; + } + set { + text = value; + } + } + + /// + /// Returns the xml item's documentation as retrieved from + /// the xs:annotation/xs:documentation element. + /// + public string Description { + get { + return description; + } + } + + public double Priority { + get { + return 0; + } + } + + public bool InsertAction(TextArea textArea, char ch) + { + if ((dataType == DataType.XmlElement) || (dataType == DataType.XmlAttributeValue)) { + textArea.InsertString(text); + } + else if (dataType == DataType.NamespaceUri) { + textArea.InsertString(String.Concat("\"", text, "\"")); + } else { + // Insert an attribute. + Caret caret = textArea.Caret; + textArea.InsertString(String.Concat(text, "=\"\"")); + + // Move caret into the middle of the attribute quotes. + caret.Position = textArea.Document.OffsetToPosition(caret.Offset - 1); + } + return false; + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataCollection.cs b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataCollection.cs new file mode 100644 index 0000000000..1e25ac20dd --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataCollection.cs @@ -0,0 +1,289 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.Collections; + +namespace ICSharpCode.XmlEditor +{ + /* + /// + /// A collection that stores objects. + /// + [Serializable()] + public class XmlCompletionDataCollection : CollectionBase { + + /// + /// Initializes a new instance of . + /// + public XmlCompletionDataCollection() + { + } + + /// + /// Initializes a new instance of based on another . + /// + /// + /// A from which the contents are copied + /// + public XmlCompletionDataCollection(XmlCompletionDataCollection val) + { + this.AddRange(val); + } + + /// + /// Initializes a new instance of containing any array of objects. + /// + /// + /// A array of objects with which to intialize the collection + /// + public XmlCompletionDataCollection(XmlCompletionData[] val) + { + this.AddRange(val); + } + + /// + /// Represents the entry at the specified index of the . + /// + /// The zero-based index of the entry to locate in the collection. + /// The entry at the specified index of the collection. + /// is outside the valid range of indexes for the collection. + public XmlCompletionData this[int index] { + get { + return ((XmlCompletionData)(List[index])); + } + set { + List[index] = value; + } + } + + /// + /// Adds a with the specified value to the + /// . + /// + /// + /// If the completion data already exists in the collection it is not added. + /// + /// The to add. + /// The index at which the new element was inserted. + /// + public int Add(XmlCompletionData val) + { + int index = -1; + if (!Contains(val)) { + index = List.Add(val); + } + return index; + } + + /// + /// Copies the elements of an array to the end of the . + /// + /// + /// An array of type containing the objects to add to the collection. + /// + /// + public void AddRange(XmlCompletionData[] val) + { + for (int i = 0; i < val.Length; i++) { + this.Add(val[i]); + } + } + + /// + /// Adds the contents of another to the end of the collection. + /// + /// + /// A containing the objects to add to the collection. + /// + /// + public void AddRange(XmlCompletionDataCollection val) + { + for (int i = 0; i < val.Count; i++) + { + this.Add(val[i]); + } + } + + /// + /// Gets a value indicating whether the + /// contains the specified . + /// + /// The to locate. + /// + /// if the is contained in the collection; + /// otherwise, . + /// + /// + public bool Contains(XmlCompletionData val) + { + if (val.Text != null) { + if (val.Text.Length > 0) { + return Contains(val.Text); + } + } + return false; + } + + public bool Contains(string name) + { + bool contains = false; + + foreach (XmlCompletionData data in this) { + if (data.Text != null) { + if (data.Text.Length > 0) { + if (data.Text == name) { + contains = true; + break; + } + } + } + } + + return contains; + } + + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + /// The one-dimensional that is the destination of the values copied from . + /// The index in where copying begins. + /// + /// is multidimensional. + /// -or- + /// The number of elements in the is greater than + /// the available space between and the end of + /// . + /// + /// is . + /// is less than 's lowbound. + /// + public void CopyTo(XmlCompletionData[] array, int index) + { + List.CopyTo(array, index); + } + + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + public void CopyTo(ICompletionData[] array, int index) + { + List.CopyTo(array, index); + } + + /// + /// Returns the index of a in + /// the . + /// + /// The to locate. + /// + /// The index of the of in the + /// , if found; otherwise, -1. + /// + /// + public int IndexOf(XmlCompletionData val) + { + return List.IndexOf(val); + } + + /// + /// Inserts a into the at the specified index. + /// + /// The zero-based index where should be inserted. + /// The to insert. + /// + public void Insert(int index, XmlCompletionData val) + { + List.Insert(index, val); + } + + /// + /// Returns an array of items. + /// + /// + public ICompletionData[] ToArray() + { + ICompletionData[] data = new ICompletionData[Count]; + CopyTo(data, 0); + return data; + } + + /// + /// Returns an enumerator that can iterate through the . + /// + /// + public new XmlCompletionDataEnumerator GetEnumerator() + { + return new XmlCompletionDataEnumerator(this); + } + + /// + /// Removes a specific from the . + /// + /// The to remove from the . + /// is not found in the Collection. + public void Remove(XmlCompletionData val) + { + List.Remove(val); + } + + /// + /// Enumerator that can iterate through a XmlCompletionDataCollection. + /// + /// + /// + /// + public class XmlCompletionDataEnumerator : IEnumerator + { + IEnumerator baseEnumerator; + IEnumerable temp; + + /// + /// Initializes a new instance of . + /// + public XmlCompletionDataEnumerator(XmlCompletionDataCollection mappings) + { + this.temp = ((IEnumerable)(mappings)); + this.baseEnumerator = temp.GetEnumerator(); + } + + /// + /// Gets the current in the . + /// + public XmlCompletionData Current { + get { + return ((XmlCompletionData)(baseEnumerator.Current)); + } + } + + object IEnumerator.Current { + get { + return baseEnumerator.Current; + } + } + + /// + /// Advances the enumerator to the next of the . + /// + public bool MoveNext() + { + return baseEnumerator.MoveNext(); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the . + /// + public void Reset() + { + baseEnumerator.Reset(); + } + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataImageList.cs b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataImageList.cs new file mode 100644 index 0000000000..9933052ae0 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataImageList.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: 915 $ +// + +using System; +using System.Windows.Forms; + +namespace ICSharpCode.XmlEditor +{ + public class XmlCompletionDataImageList + { + XmlCompletionDataImageList() + { + } + + public static ImageList GetImageList() + { + ImageList imageList = new ImageList(); + + return imageList; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataProvider.cs b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataProvider.cs new file mode 100644 index 0000000000..40c58604aa --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/CodeCompletion/XmlCompletionDataProvider.cs @@ -0,0 +1,183 @@ +// +// +// +// +// $Revision: 2760 $ +// + +using ICSharpCode.XmlBinding.Parser; +using System; +using System.Windows.Forms; +using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; + +namespace ICSharpCode.XmlEditor +{ + /* + /// + /// Provides the autocomplete (intellisense) data for an + /// xml document that specifies a known schema. + /// + public class XmlCompletionDataProvider : AbstractCompletionDataProvider + { + XmlSchemaCompletionDataCollection schemaCompletionDataItems; + XmlSchemaCompletionData defaultSchemaCompletionData; + string defaultNamespacePrefix = String.Empty; + + public XmlCompletionDataProvider(XmlSchemaCompletionDataCollection schemaCompletionDataItems, XmlSchemaCompletionData defaultSchemaCompletionData, string defaultNamespacePrefix) + { + this.schemaCompletionDataItems = schemaCompletionDataItems; + this.defaultSchemaCompletionData = defaultSchemaCompletionData; + this.defaultNamespacePrefix = defaultNamespacePrefix; + DefaultIndex = 0; + } + + public override ImageList ImageList { + get { + return XmlCompletionDataImageList.GetImageList(); + } + } + + /// + /// Overrides the default behaviour and allows special xml + /// characters such as '.' and ':' to be used as completion data. + /// + public override CompletionDataProviderKeyResult ProcessKey(char key) + { + if (key == '\r' || key == '\t') { + return CompletionDataProviderKeyResult.InsertionKey; + } + return CompletionDataProviderKeyResult.NormalKey; + } + + public override ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped) + { + preSelection = null; + string text = String.Concat(textArea.Document.GetText(0, textArea.Caret.Offset), charTyped); + + switch (charTyped) { + case '=': + // Namespace intellisense. + if (XmlParser.IsNamespaceDeclaration(text, text.Length)) { + return schemaCompletionDataItems.GetNamespaceCompletionData();; + } + break; + case '<': + // Child element intellisense. + XmlElementPath parentPath = XmlParser.GetParentElementPath(text); + if (parentPath.Elements.Count > 0) { + return GetChildElementCompletionData(parentPath); + } else if (defaultSchemaCompletionData != null) { + return defaultSchemaCompletionData.GetElementCompletionData(defaultNamespacePrefix); + } + break; + + case ' ': + // Attribute intellisense. + if (!XmlParser.IsInsideAttributeValue(text, text.Length)) { + XmlElementPath path = XmlParser.GetActiveElementStartPath(text, text.Length); + if (path.Elements.Count > 0) { + return GetAttributeCompletionData(path); + } + } + break; + + default: + + // Attribute value intellisense. + if (XmlParser.IsAttributeValueChar(charTyped)) { + string attributeName = XmlParser.GetAttributeName(text, text.Length); + if (attributeName.Length > 0) { + XmlElementPath elementPath = XmlParser.GetActiveElementStartPath(text, text.Length); + if (elementPath.Elements.Count > 0) { + preSelection = charTyped.ToString(); + return GetAttributeValueCompletionData(elementPath, attributeName); + } + } + } + break; + } + + return null; + } + + /// + /// Finds the schema given the xml element path. + /// + public XmlSchemaCompletionData FindSchema(XmlElementPath path) + { + if (path.Elements.Count > 0) { + string namespaceUri = path.Elements[0].Namespace; + if (namespaceUri.Length > 0) { + return schemaCompletionDataItems[namespaceUri]; + } else if (defaultSchemaCompletionData != null) { + + // Use the default schema namespace if none + // specified in a xml element path, otherwise + // we will not find any attribute or element matches + // later. + foreach (QualifiedName name in path.Elements) { + if (name.Namespace.Length == 0) { + name.Namespace = defaultSchemaCompletionData.NamespaceUri; + } + } + return defaultSchemaCompletionData; + } + } + return null; + } + + /// + /// Finds the schema given a namespace URI. + /// + public XmlSchemaCompletionData FindSchema(string namespaceUri) + { + return schemaCompletionDataItems[namespaceUri]; + } + + /// + /// Gets the schema completion data that was created from the specified + /// schema filename. + /// + public XmlSchemaCompletionData FindSchemaFromFileName(string fileName) + { + return schemaCompletionDataItems.GetSchemaFromFileName(fileName); + } + + ICompletionData[] GetChildElementCompletionData(XmlElementPath path) + { + ICompletionData[] completionData = null; + + XmlSchemaCompletionData schema = FindSchema(path); + if (schema != null) { + completionData = schema.GetChildElementCompletionData(path); + } + + return completionData; + } + + ICompletionData[] GetAttributeCompletionData(XmlElementPath path) + { + ICompletionData[] completionData = null; + + XmlSchemaCompletionData schema = FindSchema(path); + if (schema != null) { + completionData = schema.GetAttributeCompletionData(path); + } + + return completionData; + } + + ICompletionData[] GetAttributeValueCompletionData(XmlElementPath path, string name) + { + ICompletionData[] completionData = null; + + XmlSchemaCompletionData schema = FindSchema(path); + if (schema != null) { + completionData = schema.GetAttributeValueCompletionData(path, name); + } + + return completionData; + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddAttributeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddAttributeCommand.cs new file mode 100644 index 0000000000..1869748589 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddAttributeCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Adds a new attribute to the XML Tree's attribute property grid. + /// + public class AddAttributeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.AddAttribute(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildCommentCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildCommentCommand.cs new file mode 100644 index 0000000000..b4839eabff --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildCommentCommand.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Adds a new comment to the selected element. + /// + public class AddChildCommentCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.AppendChildComment(); + } + } + } +} + diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildElementCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildElementCommand.cs new file mode 100644 index 0000000000..2eace0388e --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildElementCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Adds a new child element to the XML Tree. + /// + public class AddChildElementCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.AddChildElement(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildTextNodeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildTextNodeCommand.cs new file mode 100644 index 0000000000..0eb25ca4d7 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AddChildTextNodeCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Adds a new text node to selected element. + /// + public class AddChildTextNodeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.AppendChildTextNode(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AssignStylesheetCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AssignStylesheetCommand.cs new file mode 100644 index 0000000000..2a1bec66ac --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/AssignStylesheetCommand.cs @@ -0,0 +1,64 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Windows.Forms; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Allows the user to browse for an XSLT stylesheet. The selected + /// stylesheet will be assigned to the currently open xml file. + /// + public class AssignStylesheetCommand : AbstractMenuCommand + { + public override void Run() + { + throw new NotImplementedException(); + // Get active xml document. +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// +// // Prompt user for filename. +// string stylesheetFileName = BrowseForStylesheetFile(); +// +// // Assign stylesheet. +// if (stylesheetFileName != null) { +// xmlView.StylesheetFileName = stylesheetFileName; +// } +// } + } + + public static string BrowseForStylesheetFile() + { + using (OpenFileDialog dialog = new OpenFileDialog()) { + dialog.AddExtension = true; + dialog.Multiselect = false; + dialog.CheckFileExists = true; + dialog.Title = ResourceService.GetString("ICSharpCode.XmlEditor.AssignXSLT.Title"); + + AddInTreeNode node = AddInTree.GetTreeNode("/SharpDevelop/Workbench/FileFilter"); + if (node != null) { + + string xmlFileFilter = (string)node.BuildChildItem("Xml", null, null); + string allFilesFilter = (string)node.BuildChildItem("AllFiles", null, null); + string xslFileFilter = (string)node.BuildChildItem("Xsl", null, null); + + dialog.Filter = String.Concat(xslFileFilter, "|", xmlFileFilter, "|", allFilesFilter); + dialog.FilterIndex = 1; + } + + if (dialog.ShowDialog(ICSharpCode.SharpDevelop.Gui.WorkbenchSingleton.MainWin32Window) == DialogResult.OK) { + return dialog.FileName; + } + } + + return null; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CodeCompletionPopupCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CodeCompletionPopupCommand.cs new file mode 100644 index 0000000000..7b4bfb1670 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CodeCompletionPopupCommand.cs @@ -0,0 +1,28 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +//using ICSharpCode.TextEditor; +//using ICSharpCode.TextEditor.Actions; + +namespace ICSharpCode.XmlEditor +{ +// /// +// /// Command executed when the user hits Ctrl+Space +// /// +// public class CodeCompletionPopupCommand : AbstractEditAction +// { +// public override void Execute(TextArea services) +// { +// throw new NotImplementedException(); +//// XmlEditorControl editor = services.MotherTextEditorControl as XmlEditorControl; +//// if (editor != null) { +//// editor.ShowCompletionWindow(); +//// } +// } +// } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CreateSchemaCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CreateSchemaCommand.cs new file mode 100644 index 0000000000..017fa3b738 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/CreateSchemaCommand.cs @@ -0,0 +1,63 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.IO; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Creates a schema based on the xml in the currently active view. + /// + public class CreateSchemaCommand : AbstractMenuCommand + { + public CreateSchemaCommand() + { + } + + public override void Run() + { + // Find active XmlView. + throw new NotImplementedException(); +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// // Create a schema based on the xml. +// string[] schemas = xmlView.InferSchema(); +// if (schemas != null) { +// // Create a new file for each generated schema. +// for (int i = 0; i < schemas.Length; ++i) { +// string fileName = GenerateSchemaFileName(xmlView.TextEditorControl.FileName, i + 1); +// OpenNewXmlFile(fileName, schemas[i]); +// } +// } +// } + } + + /// + /// Opens a new unsaved xml file in SharpDevelop. + /// + void OpenNewXmlFile(string fileName, string xml) + { + FileService.NewFile(fileName, xml); + } + + /// + /// Generates an xsd filename based on the name of the original xml file. + /// + string GenerateSchemaFileName(string xmlFileName, int count) + { + string baseFileName = Path.GetFileNameWithoutExtension(xmlFileName); + string schemaFileName = String.Concat(baseFileName, ".xsd"); + if (count == 1) { + return schemaFileName; + } + return schemaFileName = String.Concat(baseFileName, count.ToString(), ".xsd"); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/FormatXmlCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/FormatXmlCommand.cs new file mode 100644 index 0000000000..d21c136379 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/FormatXmlCommand.cs @@ -0,0 +1,28 @@ +// +// +// +// +// $Revision: -1 $ +// + +using ICSharpCode.Core; +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Pretty prints the xml. + /// + public class FormatXmlCommand : AbstractMenuCommand + { + public override void Run() + { + // Find active XmlView. + throw new NotImplementedException(); +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// xmlView.FormatXml(); +// } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/GoToSchemaDefinitionCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/GoToSchemaDefinitionCommand.cs new file mode 100644 index 0000000000..3715812097 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/GoToSchemaDefinitionCommand.cs @@ -0,0 +1,29 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Finds the definition of the Xml element or attribute under the cursor, + /// finds the schema definition for it and then opens the schema and puts the cursor + /// on the definition. + /// + public class GoToSchemaDefinitionCommand : AbstractMenuCommand + { + public override void Run() + { + throw new NotImplementedException(); +// XmlView view = XmlView.ActiveXmlView; +// if (view != null) { +// view.GoToSchemaDefinition(); +// } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentAfterCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentAfterCommand.cs new file mode 100644 index 0000000000..2ee4a29f78 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentAfterCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a new comment node after the selected node. + /// + public class InsertCommentAfterCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertCommentAfter(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentBeforeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentBeforeCommand.cs new file mode 100644 index 0000000000..ff1d61459a --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertCommentBeforeCommand.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a new comment node before the selected node. + /// + public class InsertCommentBeforeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertCommentBefore(); + } + } + } +} + diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementAfterCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementAfterCommand.cs new file mode 100644 index 0000000000..b48e1b75f6 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementAfterCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a child element after the selected element in the XML tree. + /// + public class InsertElementAfterCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertElementAfter(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementBeforeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementBeforeCommand.cs new file mode 100644 index 0000000000..7b724347e3 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertElementBeforeCommand.cs @@ -0,0 +1,26 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a child element before the selected element in the XML tree. + /// + public class InsertElementBeforeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertElementBefore(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeAfterCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeAfterCommand.cs new file mode 100644 index 0000000000..14845cdbda --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeAfterCommand.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a new text node to after the selected node. + /// + public class InsertTextNodeAfterCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertTextNodeAfter(); + } + } + } +} + diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeBeforeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeBeforeCommand.cs new file mode 100644 index 0000000000..a35db3412d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/InsertTextNodeBeforeCommand.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Inserts a new text node to before the selected node. + /// + public class InsertTextNodeBeforeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.InsertTextNodeBefore(); + } + } + } +} + diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/OpenStylesheetCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/OpenStylesheetCommand.cs new file mode 100644 index 0000000000..7a832ef2bf --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/OpenStylesheetCommand.cs @@ -0,0 +1,34 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Opens the stylesheet associated with the active XML document. + /// + public class OpenStylesheetCommand : AbstractMenuCommand + { + public override void Run() + { + throw new NotImplementedException(); +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// if (xmlView.StylesheetFileName != null) { +// try { +// FileService.OpenFile(xmlView.StylesheetFileName); +// } catch (Exception ex) { +// MessageService.ShowError(ex); +// } +// } +// } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveAttributeCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveAttributeCommand.cs new file mode 100644 index 0000000000..f1b981cf0a --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveAttributeCommand.cs @@ -0,0 +1,27 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Removes the selected attribute from the xml document being + /// displayed in the XML tree. + /// + public class RemoveAttributeCommand : AbstractMenuCommand + { + public override void Run() + { + XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; + if (view != null) { + view.RemoveAttribute(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveXPathHighlightingCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveXPathHighlightingCommand.cs new file mode 100644 index 0000000000..9215b16b68 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RemoveXPathHighlightingCommand.cs @@ -0,0 +1,23 @@ +// +// +// +// +// $Revision: 1966 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + public class RemoveXPathHighlightingCommand : AbstractMenuCommand + { + public override void Run() + { + XPathQueryPad pad = XPathQueryPad.Instance; + if (pad != null) { + pad.RemoveXPathHighlighting(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RunXslTransformCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RunXslTransformCommand.cs new file mode 100644 index 0000000000..dfa6da302d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/RunXslTransformCommand.cs @@ -0,0 +1,93 @@ +// +// +// +// +// $Revision: 2313 $ +// + +using System; +using System.IO; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Runs an XSL transform on an xml document. + /// + public class RunXslTransformCommand : AbstractCommand + { + /// + /// Runs the transform on the xml file using the assigned stylesheet. + /// If no stylesheet is assigned the user is prompted to choose one. + /// If the view represents a stylesheet that is currently assigned to an + /// opened document then run the transform on that document. + /// + public override void Run() + { + throw new NotImplementedException(); +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// +// if (xmlView is XslOutputView) { +// return; +// } +// +// // Check to see if this view is actually a referenced stylesheet. +// if (!string.IsNullOrEmpty(xmlView.PrimaryFileName)) { +// +// XmlView associatedXmlView = GetAssociatedXmlView(xmlView.PrimaryFileName); +// if (associatedXmlView != null) { +// LoggingService.Debug("Using associated xml view."); +// xmlView = associatedXmlView; +// } +// } +// +// // Assign a stylesheet. +// if (xmlView.StylesheetFileName == null) { +// xmlView.StylesheetFileName = AssignStylesheetCommand.BrowseForStylesheetFile(); +// } +// +// if (xmlView.StylesheetFileName != null) { +// try { +// xmlView.RunXslTransform(GetStylesheetContent(xmlView.StylesheetFileName)); +// } catch (Exception ex) { +// MessageService.ShowError(ex); +// } +// } +// } + } + +// /// +// /// Gets the xml view that is currently referencing the +// /// specified stylesheet view. +// /// +// XmlView GetAssociatedXmlView(string stylesheetFileName) +// { +// foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { +// XmlView view = content as XmlView; +// if (view != null && view.StylesheetFileName != null) { +// if (FileUtility.IsEqualFileName(view.StylesheetFileName, stylesheetFileName)) { +// return view; +// } +// } +// } +// return null; +// } + + string GetStylesheetContent(string fileName) + { + throw new NotImplementedException(); + // File already open? +// XmlView view = FileService.GetOpenFile(fileName) as XmlView; +// if (view != null) { +// return view.Text; +// } + + // Read in file contents. + StreamReader reader = new StreamReader(fileName, true); + return reader.ReadToEnd(); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Commands/ValidateXmlCommand.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/ValidateXmlCommand.cs new file mode 100644 index 0000000000..8104f5450d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Commands/ValidateXmlCommand.cs @@ -0,0 +1,32 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Validates the xml in the xml editor against the known schemas. + /// + public class ValidateXmlCommand : AbstractMenuCommand + { + /// + /// Validate the xml. + /// + public override void Run() + { + // Find active XmlView. + throw new NotImplementedException(); +// XmlView xmlView = XmlView.ActiveXmlView; +// if (xmlView != null) { +// // Validate the xml. +// xmlView.ValidateXml(); +// } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/AddXmlNodeDialog.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/AddXmlNodeDialog.cs new file mode 100644 index 0000000000..57fe6fc0a5 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/AddXmlNodeDialog.cs @@ -0,0 +1,341 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; +using System.Xml; + +using ICSharpCode.Core; +using ICSharpCode.Core.WinForms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Base class for the AddElementDialog and AddAttributeDialog. This + /// dialog presents a list of names and an extra text box for entering + /// a custom name. It is used to add a new node to the XML tree. It + /// contains all the core logic for the AddElementDialog and + /// AddAttributeDialog classes. + /// + public class AddXmlNodeDialog : System.Windows.Forms.Form, IAddXmlNodeDialog + { + public AddXmlNodeDialog() : this(new string[0]) + { + } + + /// + /// Creates the dialog and adds the specified names to the + /// list box. + /// + public AddXmlNodeDialog(string[] names) + { + InitializeComponent(); + InitStrings(); + if (names.Length > 0) { + AddNames(names); + } else { + RemoveNamesListBox(); + } + RightToLeftConverter.ConvertRecursive(this); + } + + /// + /// Gets the selected names in the list box together with the + /// custom name entered in the text box. + /// + public string[] GetNames() + { + // Add items selected in list box. + List names = new List(); + foreach (string name in namesListBox.SelectedItems) { + names.Add(name); + } + + // Add the custom name if entered. + string customName = customNameTextBox.Text.Trim(); + if (customName.Length > 0) { + names.Add(customName); + } + return names.ToArray(); + } + + /// + /// Gets the text from the error provider. + /// + public string GetError() + { + return errorProvider.GetError(customNameTextBox); + } + + /// + /// Gets or sets the custom name label's text. + /// + public string CustomNameLabelText { + get { + return customNameTextBoxLabel.Text; + } + set { + customNameTextBoxLabel.Text = value; + } + } + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + protected void NamesListBoxSelectedIndexChanged(object sender, EventArgs e) + { + UpdateOkButtonState(); + } + + protected void CustomNameTextBoxTextChanged(object sender, EventArgs e) + { + UpdateOkButtonState(); + } + + #region Windows Forms Designer generated code + + void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.namesListBox = new System.Windows.Forms.ListBox(); + this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.bottomPanel = new System.Windows.Forms.Panel(); + this.customNameTextBoxLabel = new System.Windows.Forms.Label(); + this.customNameTextBox = new System.Windows.Forms.TextBox(); + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit(); + this.bottomPanel.SuspendLayout(); + this.SuspendLayout(); + // + // namesListBox + // + this.namesListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.namesListBox.FormattingEnabled = true; + this.namesListBox.Location = new System.Drawing.Point(0, 0); + this.namesListBox.Name = "namesListBox"; + this.namesListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended; + this.namesListBox.Size = new System.Drawing.Size(289, 173); + this.namesListBox.Sorted = true; + this.namesListBox.TabIndex = 1; + this.namesListBox.SelectedIndexChanged += new System.EventHandler(this.NamesListBoxSelectedIndexChanged); + // + // errorProvider + // + this.errorProvider.ContainerControl = this; + // + // bottomPanel + // + this.bottomPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.bottomPanel.Controls.Add(this.customNameTextBoxLabel); + this.bottomPanel.Controls.Add(this.customNameTextBox); + this.bottomPanel.Controls.Add(this.cancelButton); + this.bottomPanel.Controls.Add(this.okButton); + this.bottomPanel.Location = new System.Drawing.Point(0, 173); + this.bottomPanel.Name = "bottomPanel"; + this.bottomPanel.Size = new System.Drawing.Size(289, 73); + this.bottomPanel.TabIndex = 2; + // + // customNameTextBoxLabel + // + this.customNameTextBoxLabel.Location = new System.Drawing.Point(3, 10); + this.customNameTextBoxLabel.Name = "customNameTextBoxLabel"; + this.customNameTextBoxLabel.Size = new System.Drawing.Size(82, 23); + this.customNameTextBoxLabel.TabIndex = 3; + this.customNameTextBoxLabel.Text = "Custom:"; + // + // customNameTextBox + // + this.customNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.customNameTextBox.Location = new System.Drawing.Point(107, 10); + this.customNameTextBox.Name = "customNameTextBox"; + this.customNameTextBox.Size = new System.Drawing.Size(167, 20); + this.customNameTextBox.TabIndex = 4; + this.customNameTextBox.TextChanged += new System.EventHandler(this.CustomNameTextBoxTextChanged); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(199, 40); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 6; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Enabled = false; + this.okButton.Location = new System.Drawing.Point(118, 40); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 5; + this.okButton.Text = "OK"; + this.okButton.UseVisualStyleBackColor = true; + // + // AddXmlNodeDialog + // + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(289, 244); + this.Controls.Add(this.bottomPanel); + this.Controls.Add(this.namesListBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(289, 143); + this.Name = "AddXmlNodeDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit(); + this.bottomPanel.ResumeLayout(false); + this.bottomPanel.PerformLayout(); + this.ResumeLayout(false); + } + private System.Windows.Forms.Panel bottomPanel; + private System.ComponentModel.IContainer components; + private System.Windows.Forms.ErrorProvider errorProvider; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.TextBox customNameTextBox; + private System.Windows.Forms.Label customNameTextBoxLabel; + private System.Windows.Forms.ListBox namesListBox; + + #endregion + + /// + /// Adds the names to the list box. + /// + void AddNames(string[] names) + { + foreach (string name in names) { + namesListBox.Items.Add(name); + } + } + + /// + /// Enables or disables the ok button depending on whether any list + /// item is selected or a custom name has been entered. + /// + void UpdateOkButtonState() + { + okButton.Enabled = IsOkButtonEnabled; + } + + /// + /// Returns whether any items are selected in the list box. + /// + bool IsItemSelected { + get { + return namesListBox.SelectedIndex >= 0; + } + } + + bool IsOkButtonEnabled { + get { + return IsItemSelected || ValidateCustomName(); + } + } + + /// + /// Returns whether there is a valid string in the custom + /// name text box. The string must be a name that can be used to + /// create an xml element or attribute. + /// + bool ValidateCustomName() + { + string name = customNameTextBox.Text.Trim(); + if (name.Length > 0) { + try { + VerifyName(name); + errorProvider.Clear(); + return true; + } catch (XmlException ex) { + errorProvider.SetError(customNameTextBox, ex.Message); + } + } + return false; + } + + /// + /// Checks that the name would make a valid element name or + /// attribute name. Trying to use XmlConvert and its Verify methods + /// so the validation is not done ourselves. XmlDocument has a + /// CheckName method but this is not public. + /// + void VerifyName(string name) + { + // Check the qualification is valid. + string[] parts = name.Split(new char[] {':'}, 2); + if (parts.Length == 1) { + // No colons. + XmlConvert.VerifyName(name); + return; + } + + string firstPart = parts[0].Trim(); + string secondPart = parts[1].Trim(); + if (firstPart.Length > 0 && secondPart.Length > 0) { + XmlConvert.VerifyNCName(firstPart); + XmlConvert.VerifyNCName(secondPart); + } else { + // Throw an error using VerifyNCName since the + // qualified name parts have no strings. + XmlConvert.VerifyNCName(name); + } + } + + /// + /// Sets the control's text using string resources. + /// + void InitStrings() + { + okButton.Text = StringParser.Parse("${res:Global.OKButtonText}"); + cancelButton.Text = StringParser.Parse("${res:Global.CancelButtonText}"); + } + + /// + /// Removes the names list box from the dialog, re-positions the + /// remaining controls and resizes the dialog to fit. + /// + void RemoveNamesListBox() + { + using (namesListBox) { + Controls.Remove(namesListBox); + + // Reset the dialog's minimum size first so setting the + // ClientSize to something smaller works as expected. + MinimumSize = Size.Empty; + ClientSize = bottomPanel.Size; + MinimumSize = Size; + + // Make sure bottom panel fills the dialog when it is resized. + bottomPanel.Dock = DockStyle.Fill; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/GoToSchemaDefinitionEditAction.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/GoToSchemaDefinitionEditAction.cs new file mode 100644 index 0000000000..495b63aae9 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/GoToSchemaDefinitionEditAction.cs @@ -0,0 +1,25 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ +// TODO : Add extendable WPF commands to allow this with AvalonEdit +// /// +// /// Finds the schema definition of the Xml element or attribute under the cursor +// /// when the user presses Control+Enter +// /// +// public class GoToSchemaDefinitionEditAction : AbstractEditAction +// { +// public override void Execute(TextArea services) +// { +// GoToSchemaDefinitionCommand goToDefinitionCommand = new GoToSchemaDefinitionCommand(); +// goToDefinitionCommand.Run(); +// } +// } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IAddXmlNodeDialog.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IAddXmlNodeDialog.cs new file mode 100644 index 0000000000..f189b0b6a7 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IAddXmlNodeDialog.cs @@ -0,0 +1,31 @@ +// +// +// +// +// $Revision: -1 $ +// + + +using System; +using System.Windows.Forms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Interface for the AddXmlNodeDialog. + /// + public interface IAddXmlNodeDialog : IDisposable + { + /// + /// The names that should be added. These are the + /// names that the user selected in the dialog when + /// it was closed. + /// + string[] GetNames(); + + /// + /// Shows the dialog. + /// + DialogResult ShowDialog(); + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IXmlTreeView.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IXmlTreeView.cs new file mode 100644 index 0000000000..616e51767e --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/IXmlTreeView.cs @@ -0,0 +1,201 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Xml; + +namespace ICSharpCode.XmlEditor +{ + public interface IXmlTreeView + { + /// + /// Displays an error message indicating that the xml tree view + /// could not display the xml since the xml is not well formed. + /// + /// The exception that occurred when the xml + /// was loaded. + void ShowXmlIsNotWellFormedMessage(XmlException ex); + + /// + /// Displays an error message. + /// + void ShowErrorMessage(string message); + + /// + /// Gets or sets whether this view needs saving. + /// + bool IsDirty {get; set;} + + /// + /// Gets or sets the xml document. + /// + XmlDocument Document {get; set;} + + /// + /// Gets the selected node in the tree. + /// + XmlNode SelectedNode {get;} + + /// + /// Gets the xml element selected. + /// + XmlElement SelectedElement {get;} + + /// + /// Shows the attributes for the selected xml element in the view. + /// + void ShowAttributes(XmlAttributeCollection attributes); + + /// + /// Removes the attributes from the view. + /// + void ClearAttributes(); + + /// + /// Shows the xml element text content. + /// + void ShowTextContent(string text); + + /// + /// Gets or sets the text content currently on display. The + /// text content will not be displayed unless + /// ShowTextContent has been called. + /// + string TextContent {get; set;} + + /// + /// Gets the xml element text node. + /// + XmlText SelectedTextNode {get;} + + /// + /// Shows the add attribute dialog and allows the user + /// to select a new attribute to be added to the selected + /// xml element. + /// + /// The list of attributes to + /// be displayed to the user. + /// The attributes selected; otherwise an empty + /// collection. + string[] SelectNewAttributes(string[] attributes); + + /// + /// Gets the name of the selected attribute. + /// + string SelectedAttribute {get;} + + /// + /// Shows the add element dialog and allows the user + /// to select a new element to be added to the selected + /// xml element, either added as a child, inserted before + /// or after. + /// + /// The list of elements to + /// be displayed to the user. + /// The attributes elements; otherwise an empty + /// collection. + string[] SelectNewElements(string[] elements); + + /// + /// Appends the child element to the currently selected element. + /// + void AppendChildElement(XmlElement element); + + /// + /// Inserts the specified element before the currently selected + /// element. + /// + void InsertElementBefore(XmlElement element); + + /// + /// Inserts the specified element after the currently selected + /// element. + /// + void InsertElementAfter(XmlElement element); + + /// + /// Removes the specified element from the tree. + /// + void RemoveElement(XmlElement element); + + /// + /// Informs the view that the specified node has been selected + /// to be cut from the tree. The view can then update its display + /// to inform the user that the node has been cut. + /// + void ShowCut(XmlNode node); + + /// + /// Informs the view that the visual indication of the cut should + /// be cleared. + /// + void HideCut(XmlNode node); + + /// + /// Appends a new child text node to the currently selected + /// element. + /// + void AppendChildTextNode(XmlText textNode); + + /// + /// Inserts a new child text node before the currently + /// selected node. + /// + void InsertTextNodeBefore(XmlText textNode); + + /// + /// Inserts a new child text node after the currently + /// selected node. + /// + void InsertTextNodeAfter(XmlText textNode); + + /// + /// Removes the currently selected text node. + /// + void RemoveTextNode(XmlText textNode); + + /// + /// Informs the xml tree view that the text node + /// has changed and the corresponding tree node + /// needs to be updated. + /// + void UpdateTextNode(XmlText textNode); + + /// + /// Gets the currently selected comment node. + /// + XmlComment SelectedComment {get;} + + /// + /// Informs the xml tree view that the comment node + /// has changed and the corresponding tree node + /// needs to be updated. + /// + void UpdateComment(XmlComment comment); + + /// + /// Appends a new child comment to the currently selected + /// element. + /// + void AppendChildComment(XmlComment comment); + + /// + /// Removes the specified comment node from the view. + /// + void RemoveComment(XmlComment comment); + + /// + /// Inserts the comment before the currently selected node. + /// + void InsertCommentBefore(XmlComment comment); + + /// + /// Inserts the comment after the currently selected node. + /// + void InsertCommentAfter(XmlComment comment); + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/SelectXmlSchemaForm.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/SelectXmlSchemaForm.cs new file mode 100644 index 0000000000..a3c1a81271 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/SelectXmlSchemaForm.cs @@ -0,0 +1,90 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Windows.Forms; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui.XmlForms; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Allows the use to choose a schema from the schemas that SharpDevelop + /// knows about. + /// + public class SelectXmlSchemaForm : XmlForm + { + ListBox schemaListBox; + string NoSchemaSelectedText = String.Empty; + + public SelectXmlSchemaForm(string[] namespaces) + { + Initialize(); + PopulateListBox(namespaces); + } + + /// + /// Gets or sets the selected schema namesace. + /// + public string SelectedNamespaceUri { + get { + string namespaceUri = schemaListBox.Text; + if (namespaceUri == NoSchemaSelectedText) { + namespaceUri = String.Empty; + } + return namespaceUri; + } + + set { + int index = 0; + if (value.Length > 0) { + index = schemaListBox.Items.IndexOf(value); + } + + // Select the option representing "no schema" if + // the value does not exist in the list box. + if (index == -1) { + index = 0; + } + + schemaListBox.SelectedIndex = index; + } + } + + protected override void SetupXmlLoader() + { + xmlLoader.StringValueFilter = new SharpDevelopStringValueFilter(); + xmlLoader.PropertyValueCreator = new SharpDevelopPropertyValueCreator(); + } + + void Initialize() + { + SetupFromXmlStream(this.GetType().Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.SelectXmlSchema.xfrm")); + + NoSchemaSelectedText = StringParser.Parse("${res:Dialog.Options.IDEOptions.TextEditor.Behaviour.IndentStyle.None}"); + + schemaListBox = (ListBox)ControlDictionary["schemaListBox"]; + + AcceptButton = (Button)ControlDictionary["okButton"]; + CancelButton = (Button)ControlDictionary["cancelButton"]; + AcceptButton.DialogResult = DialogResult.OK; + } + + void PopulateListBox(string[] namespaces) + { + foreach (string schemaNamespace in namespaces) { + schemaListBox.Items.Add(schemaNamespace); + } + + // Add the "None" string at the top of the list. + schemaListBox.Sorted = true; + schemaListBox.Sorted = false; + + schemaListBox.Items.Insert(0, NoSchemaSelectedText); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryControl.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryControl.cs new file mode 100644 index 0000000000..7456ec5944 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryControl.cs @@ -0,0 +1,704 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading; +using System.Windows.Forms; +using System.Xml; +using System.Xml.XPath; + +using ICSharpCode.Core; +using ICSharpCode.Core.WinForms; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + public class XPathQueryControl : System.Windows.Forms.UserControl, IMementoCapable + { + const int ErrorImageIndex = 0; + const string NamespacesProperty = "Namespaces"; + const string PrefixColumnWidthProperty = "NamespacesDataGridView.PrefixColumn.Width"; + const string MatchColumnWidthProperty = "XPathResultsListView.MatchColumn.Width"; + const string LineColumnWidthProperty = "XPathResultsListView.LineColumn.Width"; + const string XPathComboBoxTextProperty = "XPathQuery.LastQuery"; + const string XPathComboBoxItemsProperty = "XPathQuery.History"; + + /// + /// The filename that the last query was executed on. + /// + string fileName = String.Empty; + + /// + /// The total number of xpath queries to remember. + /// + const int xpathQueryHistoryLimit = 20; + + bool ignoreXPathTextChanges; + + enum MoveCaret { + ByJumping = 1, + ByScrolling = 2 + } + + public XPathQueryControl() + { + InitializeComponent(); + InitStrings(); + InitImageList(); + xPathComboBox.KeyDown += XPathComboBoxKeyDown; + InitAutoCompleteMode(); + } + + /// + /// Adds a namespace to the namespace list. + /// + public void AddNamespace(string prefix, string uri) + { + namespacesDataGridView.Rows.Add(new object[] {prefix, uri}); + } + + /// + /// Gets the list of namespaces in the namespace list. + /// + public ReadOnlyCollection GetNamespaces() + { + List namespaces = new List(); + for (int i = 0; i < namespacesDataGridView.Rows.Count - 1; ++i) { + DataGridViewRow row = namespacesDataGridView.Rows[i]; + string prefix = GetPrefix(row); + string uri = GetNamespace(row); + if (prefix.Length == 0 && uri.Length == 0) { + // Ignore. + } else { + namespaces.Add(new XmlNamespace(prefix, uri)); + } + } + return new ReadOnlyCollection(namespaces); + } + + public DataGridView NamespacesDataGridView { + get { + return namespacesDataGridView; + } + } + + public ListView XPathResultsListView { + get { + return xPathResultsListView; + } + } + + public ComboBox XPathComboBox { + get { + return xPathComboBox; + } + } + + /// + /// Creates a properties object that contains the current state of the + /// control. + /// + public Properties CreateMemento() + { + Properties properties = new Properties(); + + // Save namespaces. + properties.Set(NamespacesProperty, GetNamespaceStringArray()); + + // Save namespace data grid column widths. + properties.Set(PrefixColumnWidthProperty, prefixColumn.Width); + + // Save xpath results list view column widths. + properties.Set(MatchColumnWidthProperty, matchColumnHeader.Width); + properties.Set(LineColumnWidthProperty, lineColumnHeader.Width); + + // Save xpath query history. + properties.Set(XPathComboBoxTextProperty, XPathComboBox.Text); + properties.Set(XPathComboBoxItemsProperty, GetXPathHistory()); + + return properties; + } + + /// + /// Reloads the state of the control. + /// + public void SetMemento(Properties memento) + { + ignoreXPathTextChanges = true; + + try { + // Set namespaces. + string[] namespaces = memento.Get(NamespacesProperty, new string[0]); + foreach (string ns in namespaces) { + XmlNamespace xmlNamespace = XmlNamespace.FromString(ns); + AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri); + } + + // Set namespace data grid column widths. + prefixColumn.Width = memento.Get(PrefixColumnWidthProperty, 50); + + // Set xpath results list view column widths. + matchColumnHeader.Width = memento.Get(MatchColumnWidthProperty, 432); + lineColumnHeader.Width = memento.Get(LineColumnWidthProperty, 60); + + // Set xpath query history. + XPathComboBox.Text = memento.Get(XPathComboBoxTextProperty, String.Empty); + string[] xpaths = memento.Get(XPathComboBoxItemsProperty, new string[0]); + foreach (string xpath in xpaths) { + xPathComboBox.Items.Add(xpath); + } + } finally { + ignoreXPathTextChanges = false; + } + } + + /// + /// Called when the active workbench window has changed. + /// + public void ActiveWindowChanged() + { + UpdateQueryButtonState(); + } + + /// + /// Removes all the XPath Node markers from all the open documents. + /// + public void RemoveXPathNodeTextMarkers() + { + foreach (IViewContent view in WorkbenchSingleton.Workbench.ViewContentCollection) { + ITextEditorControlProvider textEditorProvider = view as ITextEditorControlProvider; + if (textEditorProvider != null) { +// XPathNodeTextMarker.RemoveMarkers(textEditorProvider.TextEditorControl.Document.MarkerStrategy); +// textEditorProvider.TextEditorControl.Refresh(); + } + } + } + + /// + /// Disposes resources used by the control. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Forms Designer generated code + + /// + /// Designer variable used to keep track of non-visual components. + /// + System.ComponentModel.IContainer components = null; + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.xPathLabel = new System.Windows.Forms.Label(); + this.xPathComboBox = new System.Windows.Forms.ComboBox(); + this.queryButton = new System.Windows.Forms.Button(); + this.tabControl = new System.Windows.Forms.TabControl(); + this.xPathResultsTabPage = new System.Windows.Forms.TabPage(); + this.xPathResultsListView = new System.Windows.Forms.ListView(); + this.matchColumnHeader = new System.Windows.Forms.ColumnHeader(); + this.lineColumnHeader = new System.Windows.Forms.ColumnHeader(); + this.imageList = new System.Windows.Forms.ImageList(this.components); + this.namespacesTabPage = new System.Windows.Forms.TabPage(); + this.namespacesDataGridView = new System.Windows.Forms.DataGridView(); + this.prefixColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.namespaceColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.tabControl.SuspendLayout(); + this.xPathResultsTabPage.SuspendLayout(); + this.namespacesTabPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.namespacesDataGridView)).BeginInit(); + this.SuspendLayout(); + // + // xPathLabel + // + this.xPathLabel.Location = new System.Drawing.Point(3, 3); + this.xPathLabel.Name = "xPathLabel"; + this.xPathLabel.Size = new System.Drawing.Size(46, 19); + this.xPathLabel.TabIndex = 0; + this.xPathLabel.Text = "XPath:"; + this.xPathLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // xPathComboBox + // + this.xPathComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.xPathComboBox.FormattingEnabled = true; + this.xPathComboBox.Location = new System.Drawing.Point(55, 3); + this.xPathComboBox.Name = "xPathComboBox"; + this.xPathComboBox.Size = new System.Drawing.Size(438, 21); + this.xPathComboBox.TabIndex = 1; + this.xPathComboBox.TextChanged += new System.EventHandler(this.XPathComboBoxTextChanged); + this.xPathComboBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.XPathComboBoxKeyDown); + // + // queryButton + // + this.queryButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.queryButton.Enabled = false; + this.queryButton.Location = new System.Drawing.Point(499, 3); + this.queryButton.Name = "queryButton"; + this.queryButton.Size = new System.Drawing.Size(70, 23); + this.queryButton.TabIndex = 2; + this.queryButton.Text = "Query"; + this.queryButton.UseVisualStyleBackColor = true; + this.queryButton.Click += new System.EventHandler(this.QueryButtonClick); + // + // tabControl + // + this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl.Controls.Add(this.xPathResultsTabPage); + this.tabControl.Controls.Add(this.namespacesTabPage); + this.tabControl.Location = new System.Drawing.Point(0, 30); + this.tabControl.Name = "tabControl"; + this.tabControl.SelectedIndex = 0; + this.tabControl.Size = new System.Drawing.Size(572, 208); + this.tabControl.TabIndex = 3; + // + // xPathResultsTabPage + // + this.xPathResultsTabPage.Controls.Add(this.xPathResultsListView); + this.xPathResultsTabPage.Location = new System.Drawing.Point(4, 22); + this.xPathResultsTabPage.Name = "xPathResultsTabPage"; + this.xPathResultsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.xPathResultsTabPage.Size = new System.Drawing.Size(564, 182); + this.xPathResultsTabPage.TabIndex = 0; + this.xPathResultsTabPage.Text = "Results"; + this.xPathResultsTabPage.UseVisualStyleBackColor = true; + // + // xPathResultsListView + // + this.xPathResultsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.matchColumnHeader, + this.lineColumnHeader}); + this.xPathResultsListView.Dock = System.Windows.Forms.DockStyle.Fill; + this.xPathResultsListView.FullRowSelect = true; + this.xPathResultsListView.HideSelection = false; + this.xPathResultsListView.Location = new System.Drawing.Point(3, 3); + this.xPathResultsListView.MultiSelect = false; + this.xPathResultsListView.Name = "xPathResultsListView"; + this.xPathResultsListView.Size = new System.Drawing.Size(558, 176); + this.xPathResultsListView.SmallImageList = this.imageList; + this.xPathResultsListView.TabIndex = 0; + this.xPathResultsListView.UseCompatibleStateImageBehavior = false; + this.xPathResultsListView.View = System.Windows.Forms.View.Details; + this.xPathResultsListView.ItemActivate += new System.EventHandler(this.XPathResultsListViewItemActivate); + this.xPathResultsListView.SelectedIndexChanged += new System.EventHandler(this.XPathResultsListViewSelectedIndexChanged); + this.xPathResultsListView.Click += new System.EventHandler(this.XPathResultsListViewClick); + // + // matchColumnHeader + // + this.matchColumnHeader.Text = "Match"; + this.matchColumnHeader.Width = 432; + // + // lineColumnHeader + // + this.lineColumnHeader.Text = "Line"; + // + // imageList + // + this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; + this.imageList.ImageSize = new System.Drawing.Size(16, 16); + this.imageList.TransparentColor = System.Drawing.Color.Transparent; + // + // namespacesTabPage + // + this.namespacesTabPage.Controls.Add(this.namespacesDataGridView); + this.namespacesTabPage.Location = new System.Drawing.Point(4, 22); + this.namespacesTabPage.Name = "namespacesTabPage"; + this.namespacesTabPage.Padding = new System.Windows.Forms.Padding(3); + this.namespacesTabPage.Size = new System.Drawing.Size(564, 182); + this.namespacesTabPage.TabIndex = 1; + this.namespacesTabPage.Text = "Namespaces"; + this.namespacesTabPage.UseVisualStyleBackColor = true; + // + // namespacesDataGridView + // + this.namespacesDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.namespacesDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.prefixColumn, + this.namespaceColumn}); + this.namespacesDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; + this.namespacesDataGridView.Location = new System.Drawing.Point(3, 3); + this.namespacesDataGridView.MultiSelect = false; + this.namespacesDataGridView.Name = "namespacesDataGridView"; + this.namespacesDataGridView.RowHeadersWidth = 25; + this.namespacesDataGridView.ShowEditingIcon = false; + this.namespacesDataGridView.Size = new System.Drawing.Size(558, 176); + this.namespacesDataGridView.TabIndex = 0; + // + // prefixColumn + // + this.prefixColumn.HeaderText = "Prefix"; + this.prefixColumn.Name = "prefixColumn"; + this.prefixColumn.Width = 50; + // + // namespaceColumn + // + this.namespaceColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + this.namespaceColumn.HeaderText = "Namespace"; + this.namespaceColumn.Name = "namespaceColumn"; + // + // XPathQueryControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tabControl); + this.Controls.Add(this.queryButton); + this.Controls.Add(this.xPathComboBox); + this.Controls.Add(this.xPathLabel); + this.Name = "XPathQueryControl"; + this.Size = new System.Drawing.Size(572, 238); + this.tabControl.ResumeLayout(false); + this.xPathResultsTabPage.ResumeLayout(false); + this.namespacesTabPage.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.namespacesDataGridView)).EndInit(); + this.ResumeLayout(false); + } + private System.Windows.Forms.ImageList imageList; + private System.Windows.Forms.DataGridViewTextBoxColumn namespaceColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn prefixColumn; + private System.Windows.Forms.DataGridView namespacesDataGridView; + private System.Windows.Forms.ColumnHeader lineColumnHeader; + private System.Windows.Forms.ColumnHeader matchColumnHeader; + private System.Windows.Forms.ListView xPathResultsListView; + private System.Windows.Forms.TabPage namespacesTabPage; + private System.Windows.Forms.TabPage xPathResultsTabPage; + private System.Windows.Forms.TabControl tabControl; + private System.Windows.Forms.Button queryButton; + private System.Windows.Forms.ComboBox xPathComboBox; + private System.Windows.Forms.Label xPathLabel; + + #endregion + + void XPathComboBoxTextChanged(object sender, EventArgs e) + { + if (!ignoreXPathTextChanges) { + UpdateQueryButtonState(); + } + } + + void UpdateQueryButtonState() + { + throw new NotImplementedException(); +// queryButton.Enabled = IsXPathQueryEntered && XmlView.IsXmlViewActive; + } + + bool IsXPathQueryEntered { + get { + return xPathComboBox.Text.Length > 0; + } + } + + void QueryButtonClick(object sender, EventArgs e) + { + RunXPathQuery(); + } + + void RunXPathQuery() + { + throw new NotImplementedException(); +// XmlView view = XmlView.ActiveXmlView; +// if (view == null) { +// return; +// } +// +// try { +// MarkerStrategy markerStrategy = view.TextEditorControl.Document.MarkerStrategy; +// fileName = view.PrimaryFileName; +// +// // Clear previous XPath results. +// ClearResults(); +// XPathNodeTextMarker.RemoveMarkers(markerStrategy); +// +// // Run XPath query. +// XPathNodeMatch[] nodes = view.SelectNodes(xPathComboBox.Text, GetNamespaces()); +// if (nodes.Length > 0) { +// AddXPathResults(nodes); +// XPathNodeTextMarker.AddMarkers(markerStrategy, nodes); +// } else { +// AddNoXPathResult(); +// } +// AddXPathToHistory(); +// } catch (XPathException xpathEx) { +// AddErrorResult(xpathEx); +// } catch (XmlException xmlEx) { +// AddErrorResult(xmlEx); +// } finally { +// BringResultsTabToFront(); +// view.TextEditorControl.Refresh(); +// } + } + + void ClearResults() + { + xPathResultsListView.Items.Clear(); + } + + void BringResultsTabToFront() + { + tabControl.SelectedTab = tabControl.TabPages[0]; + } + + void AddXPathResults(XPathNodeMatch[] nodes) + { + foreach (XPathNodeMatch node in nodes) { + ListViewItem item = new ListViewItem(node.DisplayValue); + if (node.HasLineInfo()) { + int line = node.LineNumber + 1; + item.SubItems.Add(line.ToString()); + } + item.Tag = node; + xPathResultsListView.Items.Add(item); + } + } + + void AddNoXPathResult() + { + xPathResultsListView.Items.Add(StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NoXPathResultsMessage}")); + } + + void AddErrorResult(XmlException ex) + { + ListViewItem item = new ListViewItem(ex.Message, ErrorImageIndex); + item.SubItems.Add(ex.LineNumber.ToString()); + item.Tag = ex; + xPathResultsListView.Items.Add(item); + } + + void AddErrorResult(XPathException ex) + { + ListViewItem item = new ListViewItem(String.Concat(StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathLabel}"), " ", ex.Message), ErrorImageIndex); + item.Tag = ex; + xPathResultsListView.Items.Add(item); + } + + void InitImageList() + { + try { + imageList.Images.Add(WinFormsResourceService.GetBitmap("Icons.16x16.Error")); + } catch (ResourceNotFoundException) { } + } + + void InitStrings() + { + lineColumnHeader.Text = StringParser.Parse("${res:CompilerResultView.LineText}"); + matchColumnHeader.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathMatchColumnHeaderTitle}"); + prefixColumn.HeaderText = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.PrefixColumnHeaderTitle}"); + namespaceColumn.HeaderText = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NamespaceColumnHeaderTitle}"); + queryButton.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.QueryButton}"); + xPathLabel.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.XPathLabel}"); + xPathResultsTabPage.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.ResultsTab}"); + namespacesTabPage.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.XPathQueryPad.NamespacesTab}"); + } + + void InitAutoCompleteMode() + { + try { + xPathComboBox.AutoCompleteMode = AutoCompleteMode.Suggest; + xPathComboBox.AutoCompleteSource = AutoCompleteSource.ListItems; + } catch (ThreadStateException) { } + } + + void XPathResultsListViewItemActivate(object sender, EventArgs e) + { + JumpToResultLocation(); + } + + /// + /// Switches focus to the location of the XPath query result. + /// + void JumpToResultLocation() + { + MoveCaretToResultLocation(MoveCaret.ByJumping); + } + + /// + /// Scrolls the text editor so the location of the XPath query results is visible. + /// + void ScrollToResultLocation() + { + MoveCaretToResultLocation(MoveCaret.ByScrolling); + } + + void MoveCaretToResultLocation(MoveCaret moveCaret) + { + if (xPathResultsListView.SelectedItems.Count > 0) { + ListViewItem item = xPathResultsListView.SelectedItems[0]; + XPathNodeMatch xPathNodeMatch = item.Tag as XPathNodeMatch; + XPathException xpathException = item.Tag as XPathException; + XmlException xmlException = item.Tag as XmlException; + if (xPathNodeMatch != null) { + MoveCaretToXPathNodeMatch(moveCaret, xPathNodeMatch); + } else if (xmlException != null) { + MoveCaretToXmlException(moveCaret, xmlException); + } else if (xpathException != null && moveCaret == MoveCaret.ByJumping) { + xPathComboBox.Focus(); + } + } + } + + void MoveCaretToXPathNodeMatch(MoveCaret moveCaret, XPathNodeMatch node) + { + if (moveCaret == MoveCaret.ByJumping) { + JumpTo(fileName, node.LineNumber, node.LinePosition); + } else { + ScrollTo(fileName, node.LineNumber, node.LinePosition, node.Value.Length); + } + } + + void MoveCaretToXmlException(MoveCaret moveCaret, XmlException ex) + { + int line = ex.LineNumber - 1; + int column = ex.LinePosition - 1; + if (moveCaret == MoveCaret.ByJumping) { + JumpTo(fileName, line, column); + } else { + ScrollTo(fileName, line, column); + } + } + + void JumpTo(string fileName, int line, int column) + { + FileService.JumpToFilePosition(fileName, line + 1, column + 1); + } + + /// + /// Scrolls to the specified line and column and also selects the given + /// length of text at this location. + /// + void ScrollTo(string fileName, int line, int column, int length) + { + throw new NotImplementedException(); +// XmlView view = XmlView.ActiveXmlView; +// if (view != null && IsFileNameMatch(view)) { +// TextAreaControl textAreaControl = view.TextEditorControl.ActiveTextAreaControl; +// if (length > 0 && line < textAreaControl.Document.TotalNumberOfLines) { +// SelectionManager selectionManager = textAreaControl.SelectionManager; +// selectionManager.ClearSelection(); +// TextLocation startPos = new TextLocation(column, line); +// TextLocation endPos = new TextLocation(column + length, line); +// selectionManager.SetSelection(startPos, endPos); +// } +// line = Math.Min(line, textAreaControl.Document.TotalNumberOfLines - 1); +// textAreaControl.ScrollTo(line, column); +// } + } + + void ScrollTo(string fileName, int line, int column) + { + ScrollTo(fileName, line, column, 0); + } + +// /// +// /// Tests whether the specified view matches the filename the XPath +// /// results were found in. +// /// +// bool IsFileNameMatch(XmlView view) +// { +// return FileUtility.IsEqualFileName(fileName, view.PrimaryFileName); +// } + + /// + /// Gets the namespaces and prefixes as a string array. + /// + string[] GetNamespaceStringArray() + { + List namespaces = new List(); + foreach (XmlNamespace ns in GetNamespaces()) { + namespaces.Add(ns.ToString()); + } + return namespaces.ToArray(); + } + + /// + /// Gets the previously used XPath queries from the combo box drop down list. + /// + /// + string [] GetXPathHistory() + { + List xpaths = new List(); + foreach (string xpath in xPathComboBox.Items) { + xpaths.Add(xpath); + } + return xpaths.ToArray(); + } + + /// + /// Gets the namespace prefix in the specified row. + /// + string GetPrefix(DataGridViewRow row) + { + string prefix = (string)row.Cells[0].Value; + if (prefix != null) { + return prefix; + } + return String.Empty; + } + + /// + /// Gets the namespace stored in the row. + /// + string GetNamespace(DataGridViewRow row) + { + string ns = (string)row.Cells[1].Value; + if (ns != null) { + return ns; + } + return String.Empty; + } + + /// + /// Adds the text in the combo box to the combo box drop down list. + /// + void AddXPathToHistory() + { + string newXPath = xPathComboBox.Text; + if (!xPathComboBox.Items.Contains(newXPath)) { + xPathComboBox.Items.Insert(0, newXPath); + if (xPathComboBox.Items.Count > xpathQueryHistoryLimit) { + xPathComboBox.Items.RemoveAt(xpathQueryHistoryLimit); + } + } + } + + void XPathComboBoxKeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Return) { + RunXPathQuery(); + } + } + + void XPathResultsListViewSelectedIndexChanged(object sender, EventArgs e) + { + ScrollToResultLocation(); + } + + void XPathResultsListViewClick(object sender, EventArgs e) + { + ScrollToResultLocation(); + } + } +} + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryPad.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryPad.cs new file mode 100644 index 0000000000..585112f9ec --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XPathQueryPad.cs @@ -0,0 +1,68 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Windows.Forms; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + public class XPathQueryPad : AbstractPadContent + { + public const string XPathQueryControlProperties = "XPathQueryControl.Options"; + + XPathQueryControl xPathQueryControl; + bool disposed; + static XPathQueryPad instance; + + public XPathQueryPad() + { + xPathQueryControl = new XPathQueryControl(); + WorkbenchSingleton.Workbench.ActiveViewContentChanged += ActiveViewContentChanged; + Properties properties = PropertyService.Get(XPathQueryControlProperties, new Properties()); + xPathQueryControl.SetMemento(properties); + instance = this; + } + + public static XPathQueryPad Instance { + get { + return instance; + } + } + + /// + /// The representing the pad. + /// + public override object Control { + get { + return xPathQueryControl; + } + } + + public override void Dispose() + { + if (!disposed) { + disposed = true; + WorkbenchSingleton.Workbench.ActiveViewContentChanged -= ActiveViewContentChanged; + Properties properties = xPathQueryControl.CreateMemento(); + PropertyService.Set(XPathQueryControlProperties, properties); + xPathQueryControl.Dispose(); + } + } + + public void RemoveXPathHighlighting() + { + xPathQueryControl.RemoveXPathNodeTextMarkers(); + } + + void ActiveViewContentChanged(object source, EventArgs e) + { + xPathQueryControl.ActiveWindowChanged(); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlDisplayBinding.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlDisplayBinding.cs new file mode 100644 index 0000000000..f9fc4b78fb --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlDisplayBinding.cs @@ -0,0 +1,86 @@ +// +// +// +// +// $Revision: -1 $ +// + +using ICSharpCode.XmlEditor; +using System; +using System.Collections.Generic; +using System.IO; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlBinding.Gui +{ + /// + /// Display binding for the xml editor. + /// + public class XmlDisplayBinding : ISecondaryDisplayBinding + { + public bool ReattachWhenParserServiceIsReady { + get { + throw new NotImplementedException(); + } + } + + public bool CanAttachTo(IViewContent content) + { + return content.PrimaryFileName != null && IsFileNameHandled(content.PrimaryFileName); + } + + /// + /// Returns whether the view can handle the specified file. + /// + public static bool IsFileNameHandled(string fileName) + { + return IsXmlFileExtension(Path.GetExtension(fileName)); + } + + /// + /// Checks that the file extension refers to an xml file as + /// specified in the SyntaxModes.xml file. + /// + static bool IsXmlFileExtension(string extension) + { + foreach (string currentExtension in GetXmlFileExtensions()) { + if (String.Compare(extension, currentExtension, true) == 0) { + return true; + } + } + return false; + } + + /// + /// Gets the known xml file extensions. + /// + public static string[] GetXmlFileExtensions() + { + return string[] { ".xml", ".xaml" }; + + foreach (ParserDescriptor parser in AddInTree.BuildItems("/Workspace/Parser", null, false)) { + if (parser.Codon.Id == "XmlFoldingParser") { + return parser.Supportedextensions; + } + } + +// // Did not find the XmlFoldingParser so default to those files defined by the +// // HighlightingManager. +// IHighlightingStrategy strategy = HighlightingManager.Manager.FindHighlighter("XML"); +// if (strategy != null) { +// return strategy.Extensions; +// } + + return new string[0]; + } + + public IViewContent[] CreateSecondaryViewContent(IViewContent viewContent) + { + return (new List() { + new XmlTreeView(viewContent) + }).ToArray(); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlTreeView.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlTreeView.cs new file mode 100644 index 0000000000..f28bb654fd --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XmlTreeView.cs @@ -0,0 +1,137 @@ +// +// +// +// +// $Revision: 4018 $ +// + +using System; +using System.IO; +using System.Windows.Forms; + +using ICSharpCode.Core; +using ICSharpCode.Core.WinForms; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// The secondary view content that displays the XML document as a tree view. + /// + public class XmlTreeView : AbstractViewContent, IClipboardHandler + { + XmlTreeViewContainerControl treeViewContainer = new XmlTreeViewContainerControl(); + IViewContent parent; + bool disposed; + bool ignoreDirtyChange; + + public XmlTreeView(IViewContent parent) + { + this.parent = parent; + treeViewContainer.AttributesGrid.ContextMenuStrip = MenuService.CreateContextMenu(treeViewContainer, "/AddIns/XmlEditor/XmlTree/AttributesGrid/ContextMenu"); + treeViewContainer.TreeView.ContextMenuStrip = MenuService.CreateContextMenu(treeViewContainer, "/AddIns/XmlEditor/XmlTree/ContextMenu"); + } + + #region IClipboardHandler implementation + + /// + /// Gets whether the edit menu's cut command should be enabled. + /// + public bool EnableCut { + get { + return treeViewContainer.EnableCut; + } + } + + /// + /// Gets whether the edit menu's copy command should be enabled. + /// + public bool EnableCopy { + get { + return treeViewContainer.EnableCopy; + } + } + + /// + /// Gets whether the edit menu's paste command should be enabled. + /// + public bool EnablePaste { + get { + return treeViewContainer.EnablePaste; + } + } + + /// + /// Gets whether the edit menu's delete command should be enabled. + /// + public bool EnableDelete { + get { + return treeViewContainer.EnableDelete; + } + } + + /// + /// Always returns false. + /// + public bool EnableSelectAll { + get { + return false; + } + } + + /// + /// Cuts the selected tree node. + /// + public void Cut() + { + treeViewContainer.Cut(); + } + + /// + /// Copies the selected tree node. + /// + public void Copy() + { + treeViewContainer.Copy(); + } + + /// + /// Pastes the copied or cut node as a child of the selected tree node. + /// + public void Paste() + { + treeViewContainer.Paste(); + } + + /// + /// Deletes the selected tree node. + /// + public void Delete() + { + treeViewContainer.Delete(); + } + + /// + /// Select all is not currently supported. + /// + public void SelectAll() + { + } + + #endregion + + void TreeViewContainerDirtyChanged(object source, EventArgs e) + { + if (!ignoreDirtyChange) { + this.PrimaryFile.IsDirty = treeViewContainer.IsDirty; + } + } + + public override object Control { + get { + return this.treeViewContainer; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XslOutputView.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XslOutputView.cs new file mode 100644 index 0000000000..2574534e57 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Gui/XslOutputView.cs @@ -0,0 +1,38 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + // TODO : reimplement this +// /// +// /// Displays the resulting output from an XSL transform. +// /// +// public class XslOutputView : XmlView +// { +// public XslOutputView() +// { +// TitleName = StringParser.Parse("${res:ICSharpCode.XmlEditor.XslOutputView.Title}"); +// TextEditorControl.FileName = String.Empty; +// } +// +// public static XslOutputView Instance { +// get { +// foreach (IViewContent content in WorkbenchSingleton.Workbench.ViewContentCollection) { +// if (content is XslOutputView) { +// LoggingService.Debug("XslOutputView instance exists."); +// return (XslOutputView)content; +// } +// } +// return null; +// } +// } +// } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Parser/Parser.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/Parser.cs new file mode 100644 index 0000000000..a46f39a8d1 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/Parser.cs @@ -0,0 +1,69 @@ +// +// +// +// +// $Revision: -1 $ +// + +using ICSharpCode.XmlBinding.Gui; +using System; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.XmlEditor; + +namespace ICSharpCode.XmlBinding.Parser +{ + /// + /// Parser that does nothing except return empty compilation unit + /// classes so the XmlFoldingStrategy is executed. + /// + public class Parser : IParser + { + public Parser() + { + } + + #region IParser interface + public string[] LexerTags { + get { + return null; + } + set { + } + } + + public LanguageProperties Language { + get { + return null; + } + } + + public IExpressionFinder CreateExpressionFinder(string fileName) + { + return null; + } + + public IResolver CreateResolver() + { + return null; + } + + public ICompilationUnit Parse(IProjectContent projectContent, string fileName, string fileContent) + { + DefaultCompilationUnit compilationUnit = new DefaultCompilationUnit(projectContent); + compilationUnit.FileName = fileName; + return compilationUnit; + } + + public bool CanParse(IProject project) + { + return false; + } + + public bool CanParse(string fileName) + { + return XmlDisplayBinding.IsFileNameHandled(fileName); + } + #endregion + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedName.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedName.cs new file mode 100644 index 0000000000..32554eee92 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedName.cs @@ -0,0 +1,127 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Xml; + +namespace ICSharpCode.XmlBinding.Parser +{ + /// + /// An with the namespace prefix. + /// + /// + /// The namespace prefix active for a namespace is + /// needed when an element is inserted via autocompletion. This + /// class just adds this extra information alongside the + /// . + /// + public class QualifiedName + { + XmlQualifiedName xmlQualifiedName = XmlQualifiedName.Empty; + string prefix = String.Empty; + + public QualifiedName() + { + } + + public QualifiedName(string name, string namespaceUri) + : this(name, namespaceUri, String.Empty) + { + } + + public QualifiedName(string name, string namespaceUri, string prefix) + { + xmlQualifiedName = new XmlQualifiedName(name, namespaceUri); + this.prefix = prefix; + } + + public static bool operator ==(QualifiedName lhs, QualifiedName rhs) + { + bool equals = false; + + if (((object)lhs != null) && ((object)rhs != null)) { + equals = lhs.Equals(rhs); + } else if (((object)lhs == null) && ((object)rhs == null)) { + equals = true; + } + + return equals; + } + + public static bool operator !=(QualifiedName lhs, QualifiedName rhs) + { + return !(lhs == rhs); + } + + /// + /// A qualified name is considered equal if the namespace and + /// name are the same. The prefix is ignored. + /// + public override bool Equals(object obj) + { + bool equals = false; + + QualifiedName qualifiedName = obj as QualifiedName; + if (qualifiedName != null) { + equals = xmlQualifiedName.Equals(qualifiedName.xmlQualifiedName); + } else { + XmlQualifiedName name = obj as XmlQualifiedName; + if (name != null) { + equals = xmlQualifiedName.Equals(name); + } + } + + return equals; + } + + public override int GetHashCode() + { + return xmlQualifiedName.GetHashCode(); + } + + /// + /// Gets the namespace of the qualified name. + /// + public string Namespace { + get { return xmlQualifiedName.Namespace; } + set { xmlQualifiedName = new XmlQualifiedName(xmlQualifiedName.Name, value); } + } + + /// + /// Gets the name of the element. + /// + public string Name { + get { return xmlQualifiedName.Name; } + set { xmlQualifiedName = new XmlQualifiedName(value, xmlQualifiedName.Namespace); } + } + + /// + /// Gets the namespace prefix used. + /// + public string Prefix { + get { return prefix; } + set { prefix = value; } + } + + /// + /// Returns a string that represents the QualifiedName. + /// + public override string ToString() + { + if (xmlQualifiedName.Namespace.Length > 0) { + string prefixToString = String.Empty; + if (!String.IsNullOrEmpty(prefix)) { + prefixToString = prefix + ":"; + } + return String.Concat(prefixToString, xmlQualifiedName.Name, " [", xmlQualifiedName.Namespace, "]"); + } else if (!String.IsNullOrEmpty(prefix)) { + return prefix + ":" + xmlQualifiedName.Name; + } + return xmlQualifiedName.Name; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedNameCollection.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedNameCollection.cs new file mode 100644 index 0000000000..0340f6d975 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/QualifiedNameCollection.cs @@ -0,0 +1,274 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace ICSharpCode.XmlBinding.Parser +{ + /// + /// A collection that stores objects. + /// + [Serializable()] + public class QualifiedNameCollection : Collection { + List list; + + /// + /// Initializes a new instance of . + /// + public QualifiedNameCollection() + { + this.list = new List(); + } + + /// + /// Initializes a new instance of based on another . + /// + /// + /// A from which the contents are copied + /// + public QualifiedNameCollection(QualifiedNameCollection val) + { + this.AddRange(val); + } + + /// + /// Initializes a new instance of containing any array of objects. + /// + /// + /// A array of objects with which to intialize the collection + /// + public QualifiedNameCollection(QualifiedName[] val) + { + this.AddRange(val); + } + + /// + /// Represents the entry at the specified index of the . + /// + /// The zero-based index of the entry to locate in the collection. + /// The entry at the specified index of the collection. + /// is outside the valid range of indexes for the collection. + public QualifiedName this[int index] { + get { + return ((QualifiedName)(this.list[index])); + } + set { + this.list[index] = value; + } + } + + /// + /// Adds a with the specified value to the + /// . + /// + /// The to add. + /// + public void Add(QualifiedName val) + { + this.list.Add(val); + } + + /// + /// Copies the elements of an array to the end of the . + /// + /// + /// An array of type containing the objects to add to the collection. + /// + /// + public void AddRange(QualifiedName[] val) + { + for (int i = 0; i < val.Length; i++) { + this.Add(val[i]); + } + } + + /// + /// Adds the contents of another to the end of the collection. + /// + /// + /// A containing the objects to add to the collection. + /// + /// + public void AddRange(QualifiedNameCollection val) + { + for (int i = 0; i < val.Count; i++) + { + this.Add(val[i]); + } + } + + /// + /// Gets a value indicating whether the + /// contains the specified . + /// + /// The to locate. + /// + /// if the is contained in the collection; + /// otherwise, . + /// + /// + public bool Contains(QualifiedName val) + { + return this.list.Contains(val); + } + + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + /// The one-dimensional that is the destination of the values copied from . + /// The index in where copying begins. + /// + /// is multidimensional. + /// -or- + /// The number of elements in the is greater than + /// the available space between and the end of + /// . + /// + /// is . + /// is less than 's lowbound. + /// + public void CopyTo(QualifiedName[] array, int index) + { + this.list.CopyTo(array, index); + } + + /// + /// Returns the index of a in + /// the . + /// + /// The to locate. + /// + /// The index of the of in the + /// , if found; otherwise, -1. + /// + /// + public int IndexOf(QualifiedName val) + { + return this.list.IndexOf(val); + } + + /// + /// Inserts a into the at the specified index. + /// + /// The zero-based index where should be inserted. + /// The to insert. + /// + public void Insert(int index, QualifiedName val) + { + this.list.Insert(index, val); + } + + /// + /// Returns an enumerator that can iterate through the . + /// + /// + public new QualifiedNameEnumerator GetEnumerator() + { + return new QualifiedNameEnumerator(this); + } + + /// + /// Removes a specific from the . + /// + /// The to remove from the . + /// is not found in the Collection. + public void Remove(QualifiedName val) + { + this.list.Remove(val); + } + + /// + /// Removes the last item in this collection. + /// + public void RemoveLast() + { + if (Count > 0) { + RemoveAt(Count - 1); + } + } + + /// + /// Removes the first item in the collection. + /// + public void RemoveFirst() + { + if (Count > 0) { + RemoveAt(0); + } + } + + /// + /// Gets the namespace prefix of the last item. + /// + public string LastPrefix { + get { + if (Count > 0) { + QualifiedName name = this[Count - 1]; + return name.Prefix; + } + return String.Empty; + } + } + + /// + /// Enumerator that can iterate through a QualifiedNameCollection. + /// + /// + /// + /// + public class QualifiedNameEnumerator : IEnumerator + { + IEnumerator baseEnumerator; + IEnumerable temp; + + /// + /// Initializes a new instance of . + /// + public QualifiedNameEnumerator(QualifiedNameCollection mappings) + { + this.temp = ((IEnumerable)(mappings)); + this.baseEnumerator = temp.GetEnumerator(); + } + + /// + /// Gets the current in the . + /// + public QualifiedName Current { + get { + return ((QualifiedName)(baseEnumerator.Current)); + } + } + + object IEnumerator.Current { + get { + return baseEnumerator.Current; + } + } + + /// + /// Advances the enumerator to the next of the . + /// + public bool MoveNext() + { + return baseEnumerator.MoveNext(); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the . + /// + public void Reset() + { + baseEnumerator.Reset(); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlElementPath.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlElementPath.cs new file mode 100644 index 0000000000..b90f7c5863 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlElementPath.cs @@ -0,0 +1,148 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Text; + +namespace ICSharpCode.XmlBinding.Parser +{ + /// + /// Represents the path to an xml element starting from the root of the + /// document. + /// + public class XmlElementPath + { + QualifiedNameCollection elements = new QualifiedNameCollection(); + + public XmlElementPath() + { + } + + /// + /// Gets the elements specifying the path. + /// + /// The order of the elements determines the path. + public QualifiedNameCollection Elements { + get { return elements; } + } + + /// + /// Compacts the path so it only contains the elements that are from + /// the namespace of the last element in the path. + /// + /// This method is used when we need to know the path for a + /// particular namespace and do not care about the complete path. + /// + public void Compact() + { + if (elements.Count > 0) { + QualifiedName lastName = Elements[Elements.Count - 1]; + if (lastName != null) { + int index = FindNonMatchingParentElement(lastName.Namespace); + if (index != -1) { + RemoveParentElements(index); + } + } + } + } + + /// + /// An xml element path is considered to be equal if + /// each path item has the same name and namespace. + /// + public override bool Equals(object obj) + { + if (!(obj is XmlElementPath)) return false; + if (this == obj) return true; + + XmlElementPath rhs = (XmlElementPath)obj; + if (elements.Count == rhs.elements.Count) { + + for (int i = 0; i < elements.Count; ++i) { + if (!elements[i].Equals(rhs.elements[i])) { + return false; + } + } + return true; + } + + return false; + } + + public override int GetHashCode() + { + return elements.GetHashCode(); + } + + /// + /// Gets a string that represents the XmlElementPath. + /// + public override string ToString() + { + if (elements.Count > 0) { + StringBuilder toString = new StringBuilder(); + int lastIndex = elements.Count - 1; + for (int i = 0; i < elements.Count; ++i) { + string elementToString = GetElementToString(elements[i]); + if (i == lastIndex) { + toString.Append(elementToString); + } else { + toString.Append(elementToString); + toString.Append(" > "); + } + } + return toString.ToString(); + } + return String.Empty; + } + + /// + /// Removes elements up to and including the specified index. + /// + void RemoveParentElements(int index) + { + while (index >= 0) { + --index; + elements.RemoveFirst(); + } + } + + /// + /// Finds the first parent that does belong in the specified + /// namespace. + /// + int FindNonMatchingParentElement(string namespaceUri) + { + int index = -1; + + if (elements.Count > 1) { + // Start the check from the the last but one item. + for (int i = elements.Count - 2; i >= 0; --i) { + QualifiedName name = elements[i]; + if (name.Namespace != namespaceUri) { + index = i; + break; + } + } + } + return index; + } + + /// + /// Returns the qualified name as a string. If the name has a + /// prefix then it returns "prefix:element" otherwise it returns + /// just the element name. + /// + static string GetElementToString(QualifiedName name) + { + if (name.Prefix.Length > 0) { + return name.Prefix + ":" + name.Name; + } + return name.Name; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlParser.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlParser.cs new file mode 100644 index 0000000000..780234f279 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Parser/XmlParser.cs @@ -0,0 +1,799 @@ +// +// +// +// +// $Revision: 3977 $ +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; + +namespace ICSharpCode.XmlBinding.Parser +{ + /// + /// Utility class that contains xml parsing routines used to determine + /// the currently selected element so we can provide intellisense. + /// + /// + /// All of the routines return objects + /// since we are interested in the complete path or tree to the + /// currently active element. + /// + public class XmlParser + { + /// + /// Helper class. Holds the namespace URI and the prefix currently + /// in use for this namespace. + /// + class NamespaceURI + { + string namespaceURI = String.Empty; + string prefix = String.Empty; + + public NamespaceURI() + { + } + + public NamespaceURI(string namespaceURI, string prefix) + { + this.namespaceURI = namespaceURI; + this.prefix = prefix; + } + + public string Namespace { + get { return namespaceURI; } + set { namespaceURI = value; } + } + + public string Prefix { + get { return prefix; } + set { + prefix = value; + if (prefix == null) { + prefix = String.Empty; + } + } + } + + public override string ToString() + { + if (!String.IsNullOrEmpty(prefix)) { + return prefix + ":" + namespaceURI; + } + return namespaceURI; + } + } + + static readonly char[] whitespaceCharacters = new char[] {' ', '\n', '\t', '\r'}; + + XmlParser() + { + } + + /// + /// Gets path of the xml element start tag that the specified + /// is currently inside. + /// + /// If the index outside the start tag then an empty path + /// is returned. + public static XmlElementPath GetActiveElementStartPath(string xml, int index) + { + QualifiedNameCollection namespaces = new QualifiedNameCollection(); + return GetActiveElementStartPath(xml, index, namespaces); + } + + /// + /// Gets path of the xml element start tag that the specified + /// is currently located. This is different to the + /// GetActiveElementStartPath method since the index can be inside the element + /// name. + /// + /// If the index outside the start tag then an empty path + /// is returned. + public static XmlElementPath GetActiveElementStartPathAtIndex(string xml, int index) + { + QualifiedNameCollection namespaces = new QualifiedNameCollection(); + return GetActiveElementStartPathAtIndex(xml, index, namespaces); + } + + /// + /// Gets the parent element path based on the index position. + /// + public static XmlElementPath GetParentElementPath(string xml) + { + QualifiedNameCollection namespaces = new QualifiedNameCollection(); + XmlElementPath path = GetFullParentElementPath(xml, namespaces); + path.Compact(); + return path; + } + + /// + /// Checks whether the attribute at the end of the string is a + /// namespace declaration. + /// + public static bool IsNamespaceDeclaration(string xml, int index) + { + if (String.IsNullOrEmpty(xml)) { + return false; + } + + index = GetCorrectedIndex(xml.Length, index); + + // Move back one character if the last character is an '=' + if (xml[index] == '=') { + xml = xml.Substring(0, xml.Length - 1); + --index; + } + + // From the end of the string work backwards until we have + // picked out the last attribute and reached some whitespace. + StringBuilder reversedAttributeName = new StringBuilder(); + + bool ignoreWhitespace = true; + int currentIndex = index; + for (int i = 0; i < index; ++i) { + + char currentChar = xml[currentIndex]; + + if (Char.IsWhiteSpace(currentChar)) { + if (ignoreWhitespace == false) { + // Reached the start of the attribute name. + break; + } + } else if (Char.IsLetterOrDigit(currentChar) || (currentChar == ':')) { + ignoreWhitespace = false; + reversedAttributeName.Append(currentChar); + } else { + // Invalid string. + break; + } + + --currentIndex; + } + + // Did we get a namespace? + + bool isNamespace = false; + + if ((reversedAttributeName.ToString() == "snlmx") || (reversedAttributeName.ToString().EndsWith(":snlmx"))) { + isNamespace = true; + } + + return isNamespace; + } + + /// + /// Gets the attribute name and any prefix. The namespace + /// is not determined. + /// + /// if no attribute name can + /// be found. + public static QualifiedName GetQualifiedAttributeName(string xml, int index) + { + string name = GetAttributeName(xml, index); + return GetQualifiedName(name); + } + + /// + /// Gets the name of the attribute inside but before the specified + /// index. + /// + public static string GetAttributeName(string xml, int index) + { + if (String.IsNullOrEmpty(xml)) { + return String.Empty; + } + + index = GetCorrectedIndex(xml.Length, index); + return GetAttributeName(xml, index, true, true, true); + } + + /// + /// Gets the name of the attribute and its prefix at the specified index. The index + /// can be anywhere inside the attribute name or in the attribute value. + /// The namespace for the element containing the attribute will also be determined + /// if the includeNamespace flag is set to true. + /// + public static QualifiedName GetQualifiedAttributeNameAtIndex(string xml, int index, bool includeNamespace) + { + string name = GetAttributeNameAtIndex(xml, index); + QualifiedName qualifiedName = GetQualifiedName(name); + if (qualifiedName != null && String.IsNullOrEmpty(qualifiedName.Namespace) && includeNamespace) { + QualifiedNameCollection namespaces = new QualifiedNameCollection(); + XmlElementPath path = GetActiveElementStartPathAtIndex(xml, index, namespaces); + qualifiedName.Namespace = GetNamespaceForPrefix(namespaces, path.Elements.LastPrefix); + } + return qualifiedName; + } + + /// + /// Gets the name of the attribute and its prefix at the specified index. The index + /// can be anywhere inside the attribute name or in the attribute value. + /// + public static QualifiedName GetQualifiedAttributeNameAtIndex(string xml, int index) + { + return GetQualifiedAttributeNameAtIndex(xml, index, false); + } + + /// + /// Gets the name of the attribute at the specified index. The index + /// can be anywhere inside the attribute name or in the attribute value. + /// + public static string GetAttributeNameAtIndex(string xml, int index) + { + if (String.IsNullOrEmpty(xml)) { + return String.Empty; + } + + index = GetCorrectedIndex(xml.Length, index); + + bool ignoreWhitespace = true; + bool ignoreEqualsSign = false; + bool ignoreQuote = false; + + if (IsInsideAttributeValue(xml, index)) { + // Find attribute name start. + int elementStartIndex = GetActiveElementStartIndex(xml, index); + if (elementStartIndex == -1) { + return String.Empty; + } + + // Find equals sign. + for (int i = index; i > elementStartIndex; --i) { + char ch = xml[i]; + if (ch == '=') { + index = i; + ignoreEqualsSign = true; + break; + } + } + } else { + // Find end of attribute name. + for (; index < xml.Length; ++index) { + char ch = xml[index]; + if (!IsXmlNameChar(ch)) { + if (ch == '\'' || ch == '\"') { + ignoreQuote = true; + ignoreEqualsSign = true; + } + break; + } + } + --index; + } + + return GetAttributeName(xml, index, ignoreWhitespace, ignoreQuote, ignoreEqualsSign); + } + + /// + /// Checks for valid xml attribute value character + /// + public static bool IsAttributeValueChar(char ch) + { + if ((ch == '<') || + (ch == '>')) + { + return false; + } + + return true; + } + + /// + /// Checks for valid xml element or attribute name character. + /// + public static bool IsXmlNameChar(char ch) + { + if (Char.IsLetterOrDigit(ch) || + (ch == ':') || + (ch == '/') || + (ch == '_') || + (ch == '.') || + (ch == '-')) + { + return true; + } + + return false; + } + + /// + /// Determines whether the specified index is inside an attribute value. + /// + public static bool IsInsideAttributeValue(string xml, int index) + { + if (String.IsNullOrEmpty(xml)) { + return false; + } + + if (index > xml.Length) { + index = xml.Length; + } + + int elementStartIndex = GetActiveElementStartIndex(xml, index); + if (elementStartIndex == -1) { + return false; + } + + // Count the number of double quotes and single quotes that exist + // before the first equals sign encountered going backwards to + // the start of the active element. + bool foundEqualsSign = false; + int doubleQuotesCount = 0; + int singleQuotesCount = 0; + char lastQuoteChar = ' '; + for (int i = index - 1; i > elementStartIndex; --i) { + char ch = xml[i]; + if (ch == '=') { + foundEqualsSign = true; + break; + } else if (ch == '\"') { + lastQuoteChar = ch; + ++doubleQuotesCount; + } else if (ch == '\'') { + lastQuoteChar = ch; + ++singleQuotesCount; + } + } + + bool isInside = false; + + if (foundEqualsSign) { + // Odd number of quotes? + if ((lastQuoteChar == '\"') && ((doubleQuotesCount % 2) > 0)) { + isInside = true; + } else if ((lastQuoteChar == '\'') && ((singleQuotesCount %2) > 0)) { + isInside = true; + } + } + + return isInside; + } + + /// + /// Gets the attribute value at the specified index. + /// + /// An empty string if no attribute value can be found. + public static string GetAttributeValueAtIndex(string xml, int index) + { + if (!IsInsideAttributeValue(xml, index)) { + return String.Empty; + } + + index = GetCorrectedIndex(xml.Length, index); + + int elementStartIndex = GetActiveElementStartIndex(xml, index); + if (elementStartIndex == -1) { + return String.Empty; + } + + // Find equals sign. + int equalsSignIndex = -1; + for (int i = index; i > elementStartIndex; --i) { + char ch = xml[i]; + if (ch == '=') { + equalsSignIndex = i; + break; + } + } + + if (equalsSignIndex == -1) { + return String.Empty; + } + + // Find attribute value. + char quoteChar = ' '; + bool foundQuoteChar = false; + StringBuilder attributeValue = new StringBuilder(); + for (int i = equalsSignIndex; i < xml.Length; ++i) { + char ch = xml[i]; + if (!foundQuoteChar) { + if (ch == '\"' || ch == '\'') { + quoteChar = ch; + foundQuoteChar = true; + } + } else { + if (ch == quoteChar) { + // End of attribute value. + return attributeValue.ToString(); + } else if (IsAttributeValueChar(ch) || (ch == '\"' || ch == '\'')) { + attributeValue.Append(ch); + } else { + // Invalid character found. + return String.Empty; + } + } + } + + return String.Empty; + } + + /// + /// Gets the text of the xml element start tag that the index is + /// currently inside. + /// + /// + /// Returns the text up to and including the start tag < character. + /// + static string GetActiveElementStartText(string xml, int index) + { + int elementStartIndex = GetActiveElementStartIndex(xml, index); + if (elementStartIndex >= 0) { + if (elementStartIndex < index) { + int elementEndIndex = GetActiveElementEndIndex(xml, index); + if (elementEndIndex >= index) { + return xml.Substring(elementStartIndex, elementEndIndex - elementStartIndex); + } + } + } + return null; + } + + /// + /// Locates the index of the start tag < character. + /// + /// + /// Returns the index of the start tag character; otherwise + /// -1 if no start tag character is found or a end tag + /// > character is found first. + /// + public static int GetActiveElementStartIndex(string xml, int index) + { + int elementStartIndex = -1; + + int currentIndex = index - 1; + + for (int i = 0; i < index; ++i) { + + char currentChar = xml[currentIndex]; + if (currentChar == '<') { + elementStartIndex = currentIndex; + break; + } else if (currentChar == '>') { + break; + } + + --currentIndex; + } + + return elementStartIndex; + } + + /// + /// Locates the index of the end tag character. + /// + /// + /// Returns the index of the end tag character; otherwise + /// -1 if no end tag character is found or a start tag + /// character is found first. + /// + static int GetActiveElementEndIndex(string xml, int index) + { + int elementEndIndex = index; + + for (int i = index; i < xml.Length; ++i) { + + char currentChar = xml[i]; + if (currentChar == '>') { + elementEndIndex = i; + break; + } else if (currentChar == '<'){ + elementEndIndex = -1; + break; + } + } + + return elementEndIndex; + } + + /// + /// Gets the element name from the element start tag string. + /// + /// This string must start at the + /// element we are interested in. + static QualifiedName GetElementName(string xml) + { + string name = String.Empty; + + // Find the end of the element name. + xml = xml.Replace("\r\n", " "); + int index = xml.IndexOf(' '); + if (index > 0) { + name = xml.Substring(1, index - 1); + } else { + name = xml.Substring(1); + } + + return GetQualifiedName(name); + } + + /// + /// Gets the element namespace from the element start tag + /// string. + /// + /// This string must start at the + /// element we are interested in. + static NamespaceURI GetElementNamespace(string xml) + { + NamespaceURI namespaceURI = new NamespaceURI(); + + Match match = Regex.Match(xml, ".*?(xmlns\\s*?|xmlns:.*?)=\\s*?['\\\"](.*?)['\\\"]"); + if (match.Success) { + namespaceURI.Namespace = match.Groups[2].Value; + + string xmlns = match.Groups[1].Value.Trim(); + int prefixIndex = xmlns.IndexOf(':'); + if (prefixIndex > 0) { + namespaceURI.Prefix = xmlns.Substring(prefixIndex + 1); + } + } + + return namespaceURI; + } + + static string ReverseString(string text) + { + StringBuilder reversedString = new StringBuilder(text); + + int index = text.Length; + foreach (char ch in text) { + --index; + reversedString[index] = ch; + } + + return reversedString.ToString(); + } + + /// + /// Ensures that the index is on the last character if it is + /// too large. + /// + /// The length of the string. + /// The current index. + /// The index unchanged if the index is smaller than the + /// length of the string; otherwise it returns length - 1. + static int GetCorrectedIndex(int length, int index) + { + if (index >= length) { + index = length - 1; + } + return index; + } + + /// + /// Gets the active element path given the element text. + /// + static XmlElementPath GetActiveElementStartPath(string xml, int index, string elementText, QualifiedNameCollection namespaces) + { + QualifiedName elementName = GetElementName(elementText); + if (elementName == null) { + return new XmlElementPath(); + } + + NamespaceURI elementNamespace = GetElementNamespace(elementText); + + XmlElementPath path = GetFullParentElementPath(xml.Substring(0, index), namespaces); + + // Try to get a namespace for the active element's prefix. + if (elementName.Prefix.Length > 0 && elementNamespace.Namespace.Length == 0) { + elementName.Namespace = GetNamespaceForPrefix(namespaces, elementName.Prefix); + elementNamespace.Namespace = elementName.Namespace; + elementNamespace.Prefix = elementName.Prefix; + } + + if (elementNamespace.Namespace.Length == 0) { + if (path.Elements.Count > 0) { + QualifiedName parentName = path.Elements[path.Elements.Count - 1]; + elementNamespace.Namespace = parentName.Namespace; + elementNamespace.Prefix = parentName.Prefix; + } + } + path.Elements.Add(new QualifiedName(elementName.Name, elementNamespace.Namespace, elementNamespace.Prefix)); + path.Compact(); + return path; + } + + static string GetAttributeName(string xml, int index, bool ignoreWhitespace, bool ignoreQuote, bool ignoreEqualsSign) + { + string name = String.Empty; + + // From the end of the string work backwards until we have + // picked out the attribute name. + StringBuilder reversedAttributeName = new StringBuilder(); + + int currentIndex = index; + bool invalidString = true; + + for (int i = 0; i <= index; ++i) { + + char currentChar = xml[currentIndex]; + + if (IsXmlNameChar(currentChar)) { + if (!ignoreEqualsSign) { + ignoreWhitespace = false; + reversedAttributeName.Append(currentChar); + } + } else if (Char.IsWhiteSpace(currentChar)) { + if (ignoreWhitespace == false) { + // Reached the start of the attribute name. + invalidString = false; + break; + } + } else if ((currentChar == '\'') || (currentChar == '\"')) { + if (ignoreQuote) { + ignoreQuote = false; + } else { + break; + } + } else if (currentChar == '='){ + if (ignoreEqualsSign) { + ignoreEqualsSign = false; + } else { + break; + } + } else if (IsAttributeValueChar(currentChar)) { + if (!ignoreQuote) { + break; + } + } else { + break; + } + + --currentIndex; + } + + if (!invalidString) { + name = ReverseString(reversedAttributeName.ToString()); + } + + return name; + } + + /// + /// Gets the element name at the specified index. + /// + static string GetElementNameAtIndex(string xml, int index) + { + int elementStartIndex = GetActiveElementStartIndex(xml, index); + if (elementStartIndex >= 0 && elementStartIndex < index) { + int elementEndIndex = GetActiveElementEndIndex(xml, index); + if (elementEndIndex == -1) { + elementEndIndex = xml.IndexOfAny(whitespaceCharacters, elementStartIndex); + } + if (elementEndIndex >= elementStartIndex) { + return xml.Substring(elementStartIndex, elementEndIndex - elementStartIndex); + } + } + return null; + } + + /// + /// Returns a name and its prefix. + /// + static QualifiedName GetQualifiedName(string name) + { + if (name.Length == 0) { + return null; + } + + QualifiedName qualifiedName = new QualifiedName(); + int prefixIndex = name.IndexOf(':'); + if (prefixIndex > 0) { + qualifiedName.Prefix = name.Substring(0, prefixIndex); + qualifiedName.Name = name.Substring(prefixIndex + 1); + } else { + qualifiedName.Name = name; + } + return qualifiedName; + } + + /// + /// Gets the parent element path based on the index position. This + /// method does not compact the path so it will include all elements + /// including those in another namespace in the path. + /// + static XmlElementPath GetFullParentElementPath(string xml, QualifiedNameCollection namespaces) + { + XmlElementPath path = new XmlElementPath(); + IDictionary namespacesInScope = null; + using (StringReader reader = new StringReader(xml)) { + using (XmlTextReader xmlReader = new XmlTextReader(reader)) { + try { + xmlReader.XmlResolver = null; // prevent XmlTextReader from loading external DTDs + while (xmlReader.Read()) { + switch (xmlReader.NodeType) { + case XmlNodeType.Element: + if (!xmlReader.IsEmptyElement) { + QualifiedName elementName = new QualifiedName(xmlReader.LocalName, xmlReader.NamespaceURI, xmlReader.Prefix); + path.Elements.Add(elementName); + } + break; + case XmlNodeType.EndElement: + path.Elements.RemoveLast(); + break; + } + } + } catch (XmlException) { + namespacesInScope = xmlReader.GetNamespacesInScope(XmlNamespaceScope.All); + } + } + } + + // Add namespaces in scope for the last element read. + if (namespacesInScope != null) { + foreach (KeyValuePair ns in namespacesInScope) { + namespaces.Add(new QualifiedName(String.Empty, ns.Value, ns.Key)); + } + } + + return path; + } + + /// + /// Finds the namespace for the specified prefix. + /// + static string GetNamespaceForPrefix(QualifiedNameCollection namespaces, string prefix) + { + foreach (QualifiedName name in namespaces) { + if (name.Prefix == prefix) { + return name.Namespace; + } + } + return String.Empty; + } + + /// + /// Gets path of the xml element start tag that the specified + /// is currently inside. + /// + /// If the index outside the start tag then an empty path + /// is returned. + /// Returns the namespaces that are + /// exist in the xml. + static XmlElementPath GetActiveElementStartPath(string xml, int index, QualifiedNameCollection namespaces) + { + XmlElementPath path = new XmlElementPath(); + string elementText = GetActiveElementStartText(xml, index); + if (elementText != null) { + path = GetActiveElementStartPath(xml, index, elementText, namespaces); + } + return path; + } + + /// + /// Gets path of the xml element start tag that the specified + /// is currently located. This is different to the + /// GetActiveElementStartPath method since the index can be inside the element + /// name. + /// + /// If the index outside the start tag then an empty path + /// is returned. + static XmlElementPath GetActiveElementStartPathAtIndex(string xml, int index, QualifiedNameCollection namespaces) + { + // Find first non xml element name character to the right of the index. + index = GetCorrectedIndex(xml.Length, index); + if (index < 0) // can happen when xml.Length==0 + return new XmlElementPath(); + int currentIndex = index; + for (; currentIndex < xml.Length; ++currentIndex) { + char ch = xml[currentIndex]; + if (!IsXmlNameChar(ch)) { + break; + } + } + + string elementText = GetElementNameAtIndex(xml, currentIndex); + if (elementText != null) { + return GetActiveElementStartPath(xml, currentIndex, elementText, namespaces); + } + return new XmlElementPath(); + } + } +} + + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeMatch.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeMatch.cs new file mode 100644 index 0000000000..bbb9b6ca52 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeMatch.cs @@ -0,0 +1,167 @@ +// +// +// +// +// $Revision: 1662 $ +// + +using System; +using System.Xml; +using System.Xml.XPath; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Stores an XmlNode and its associated line number and position after an + /// XPath query has been evaluated. + /// + public class XPathNodeMatch : IXmlLineInfo + { + int? lineNumber; + int linePosition; + string value; + string displayValue; + XPathNodeType nodeType; + + /// + /// Creates an XPathNodeMatch from the navigator which should be position on the + /// node. + /// + /// + /// We deliberately use the OuterXml when we find a Namespace since the + /// navigator location returned starts from the xmlns attribute. + /// + public XPathNodeMatch(XPathNavigator currentNavigator) + { + SetLineNumbers(currentNavigator as IXmlLineInfo); + nodeType = currentNavigator.NodeType; + switch (nodeType) { + case XPathNodeType.Text: + SetTextValue(currentNavigator); + break; + case XPathNodeType.Comment: + SetCommentValue(currentNavigator); + break; + case XPathNodeType.Namespace: + SetNamespaceValue(currentNavigator); + break; + case XPathNodeType.Element: + SetElementValue(currentNavigator); + break; + case XPathNodeType.ProcessingInstruction: + SetProcessingInstructionValue(currentNavigator); + break; + case XPathNodeType.Attribute: + SetAttributeValue(currentNavigator); + break; + default: + value = currentNavigator.LocalName; + displayValue = value; + break; + } + } + + /// + /// Line numbers are zero based. + /// + public int LineNumber { + get { + return lineNumber.GetValueOrDefault(0); + } + } + + /// + /// Line positions are zero based. + /// + public int LinePosition { + get { + return linePosition; + } + } + + public bool HasLineInfo() + { + return lineNumber.HasValue; + } + + /// + /// Gets the text value of the node. + /// + public string Value { + get { + return value; + } + } + + /// + /// Gets the node display value. This includes the angle brackets if it is + /// an element, for example. + /// + public string DisplayValue { + get { + return displayValue; + } + } + + public XPathNodeType NodeType { + get { + return nodeType; + } + } + + void SetElementValue(XPathNavigator navigator) + { + value = navigator.Name; + if (navigator.IsEmptyElement) { + displayValue = String.Concat("<", value, "/>"); + } else { + displayValue = String.Concat("<", value, ">"); + } + } + + void SetTextValue(XPathNavigator navigator) + { + value = navigator.Value; + displayValue = value; + } + + void SetCommentValue(XPathNavigator navigator) + { + value = navigator.Value; + displayValue = navigator.OuterXml; + } + + void SetNamespaceValue(XPathNavigator navigator) + { + value = navigator.OuterXml; + displayValue = value; + } + + void SetProcessingInstructionValue(XPathNavigator navigator) + { + value = navigator.Name; + displayValue = navigator.OuterXml; + } + + void SetAttributeValue(XPathNavigator navigator) + { + value = navigator.Name; + displayValue = String.Concat("@", value); + } + + /// + /// Takes one of the xml line number so the numbers are now zero + /// based instead of one based. + /// + /// A namespace query (e.g. //namespace::*) will return + /// a line info of -1, -1 for the xml namespace. Which looks like + /// a bug in the XPathDocument class. + void SetLineNumbers(IXmlLineInfo lineInfo) + { + if (lineInfo.HasLineInfo() && lineInfo.LineNumber > 0) { + lineNumber = lineInfo.LineNumber - 1; + linePosition = lineInfo.LinePosition - 1; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeTextMarker.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeTextMarker.cs new file mode 100644 index 0000000000..dc38b58ba9 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XPathNodeTextMarker.cs @@ -0,0 +1,60 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.Drawing; + +namespace ICSharpCode.XmlEditor +{ +/* + /// + /// A text marker for an XPath query match. + /// + public class XPathNodeTextMarker : TextMarker + { + public static readonly Color MarkerBackColor = Color.FromArgb(159, 255, 162); + + public XPathNodeTextMarker(int offset, XPathNodeMatch node) : base(offset, node.Value.Length, TextMarkerType.SolidBlock, MarkerBackColor) + { + } + + /// + /// Adds markers for each XPathNodeMatch. + /// + public static void AddMarkers(MarkerStrategy markerStrategy, XPathNodeMatch[] nodes) + { + foreach (XPathNodeMatch node in nodes) { + AddMarker(markerStrategy, node); + } + } + + /// + /// Adds a single marker for the XPathNodeMatch. + /// + public static void AddMarker(MarkerStrategy markerStrategy, XPathNodeMatch node) + { + if (node.HasLineInfo() && node.Value.Length > 0) { + LineSegment lineSegment = markerStrategy.Document.GetLineSegment(node.LineNumber); + markerStrategy.AddMarker(new XPathNodeTextMarker(lineSegment.Offset + node.LinePosition, node)); + } + } + + /// + /// Removes all the XPathNodeMarkers from the marker strategy. + /// + public static void RemoveMarkers(MarkerStrategy markerStrategy) + { + markerStrategy.RemoveAll(IsXPathNodeTextMarkerMatch); + } + + static bool IsXPathNodeTextMarkerMatch(TextMarker marker) + { + return marker is XPathNodeTextMarker; + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributePropertyDescriptor.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributePropertyDescriptor.cs new file mode 100644 index 0000000000..9875efde6d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributePropertyDescriptor.cs @@ -0,0 +1,95 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Xml; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Property descriptor for an XmlAttribute. This is used when displaying + /// an XmlAttribute in the property grid. + /// + public class XmlAttributePropertyDescriptor : PropertyDescriptor + { + XmlAttribute xmlAttribute; + public XmlAttributePropertyDescriptor(XmlAttribute xmlAttribute) + : base(xmlAttribute.LocalName, new Attribute[0]) + { + this.xmlAttribute = xmlAttribute; + } + + /// + /// Gets the property descriptors for the specified attributes. + /// + public static PropertyDescriptorCollection GetProperties(XmlAttributeCollection xmlAttributes) + { + List properties = new List(); + foreach (XmlAttribute xmlAttribute in xmlAttributes) { + properties.Add(new XmlAttributePropertyDescriptor(xmlAttribute)); + } + return new PropertyDescriptorCollection(properties.ToArray()); + } + + public override Type ComponentType { + get { + return typeof(String); + } + } + + public override bool IsReadOnly { + get { + return false; + } + } + + /// + /// Returns the property type in this case a string. + /// + public override Type PropertyType { + get { + return typeof(String); + } + } + + public override bool CanResetValue(object component) + { + return false; + } + + /// + /// Gets the value of the xml attribute. + /// + public override object GetValue(object component) + { + return xmlAttribute.Value; + } + + public override void ResetValue(object component) + { + } + + /// + /// Sets the xml attribute value. + /// + public override void SetValue(object component, object value) + { + xmlAttribute.Value = (String)value; + } + + /// + /// If the current value has changed from the default value then this + /// method will return true. + /// + public override bool ShouldSerializeValue(object component) + { + return true; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributeTypeDescriptor.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributeTypeDescriptor.cs new file mode 100644 index 0000000000..18a13f171b --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlAttributeTypeDescriptor.cs @@ -0,0 +1,94 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.ComponentModel; +using System.Xml; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Type descriptor that allows us to display properties in the property grid + /// for Xml attributes. + /// + public class XmlAttributeTypeDescriptor : ICustomTypeDescriptor + { + PropertyDescriptorCollection properties; + + public XmlAttributeTypeDescriptor(XmlAttributeCollection xmlAttributes) + { + if (xmlAttributes != null) { + properties = XmlAttributePropertyDescriptor.GetProperties(xmlAttributes); + } else { + properties = new PropertyDescriptorCollection(new XmlAttributePropertyDescriptor[0]); + } + } + + public AttributeCollection GetAttributes() + { + return null; + } + + public string GetClassName() + { + return null; + } + + public string GetComponentName() + { + return null; + } + + public TypeConverter GetConverter() + { + return null; + } + + public EventDescriptor GetDefaultEvent() + { + return null; + } + + public PropertyDescriptor GetDefaultProperty() + { + return null; + } + + public object GetEditor(Type editorBaseType) + { + return null; + } + + public EventDescriptorCollection GetEvents() + { + return null; + } + + public EventDescriptorCollection GetEvents(Attribute[] attributes) + { + return null; + } + + public PropertyDescriptorCollection GetProperties() + { + return GetProperties(new Attribute[0]); + } + + public PropertyDescriptorCollection GetProperties(Attribute[] attributes) + { + return properties; + } + + /// + /// Returns this class instance. + /// + public object GetPropertyOwner(PropertyDescriptor pd) + { + return this; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCharacterDataTreeNode.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCharacterDataTreeNode.cs new file mode 100644 index 0000000000..829b99fe33 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCharacterDataTreeNode.cs @@ -0,0 +1,60 @@ +// +// +// +// +// $Revision: 2128 $ +// + +using System; +using System.Xml; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Base class for XmlTextTreeNodes and XmlCommentTreeNodes + /// + public abstract class XmlCharacterDataTreeNode : ExtTreeNode + { + XmlCharacterData characterData; + + public XmlCharacterDataTreeNode(XmlCharacterData characterData) + { + this.characterData = characterData; + } + + /// + /// Updates the display text based on changes in the + /// XmlCharacterData's InnerText associated with this node. + /// + public void Update() + { + Text = GetDisplayText(characterData.InnerText); + } + + /// + /// Gets the text to display for this tree node. + /// + /// If the text is a single line then it is returned, but + /// trimmed. If the text has multiple lines then the first line that + /// is not empty is returned. This line may have "..." appended to indicate + /// there is more text for this node that is not being displayed. The + /// "..." will be appended only if there are multiple lines containing + /// text. + static string GetDisplayText(string s) + { + string[] lines = s.Trim().Split('\n'); + for (int i = 0; i < lines.Length; ++i) { + string line = lines[i].Trim(); + if (line.Length > 0) { + if (lines.Length == 1) { + return line; + } else { + return String.Concat(line, "..."); + } + } + } + return String.Empty; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCommentTreeNode.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCommentTreeNode.cs new file mode 100644 index 0000000000..c4fe5a7661 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlCommentTreeNode.cs @@ -0,0 +1,60 @@ +// +// +// +// +// $Revision: 2164 $ +// + +using System; +using System.Xml; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Represents an xml comment in the tree. + /// + public class XmlCommentTreeNode : XmlCharacterDataTreeNode + { + public const string XmlCommentTreeNodeImageKey = "XmlCommentTreeNodeImage"; + public const string XmlCommentTreeNodeGhostImageKey = "XmlCommentTreeNodeGhostImage"; + + XmlComment comment; + + public XmlCommentTreeNode(XmlComment comment) + : base(comment) + { + this.comment = comment; + ImageKey = XmlCommentTreeNodeImageKey; + SelectedImageKey = ImageKey; + Update(); + } + + /// + /// Gets the XmlComment associated with this tree node. + /// + public XmlComment XmlComment { + get { + return comment; + } + } + + /// + /// Gets or sets whether to show the ghost image which is + /// displayed when cutting the node. + /// + public bool ShowGhostImage { + get { + return ImageKey == XmlCommentTreeNodeGhostImageKey; + } + set { + if (value) { + ImageKey = XmlCommentTreeNodeGhostImageKey; + } else { + ImageKey = XmlCommentTreeNodeImageKey; + } + SelectedImageKey = ImageKey; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorAddInOptions.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorAddInOptions.cs new file mode 100644 index 0000000000..9a442d416d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorAddInOptions.cs @@ -0,0 +1,119 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.Diagnostics; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// The Xml Editor add-in options. + /// + public static class XmlEditorAddInOptions + { + public static readonly string OptionsProperty = "XmlEditor.AddIn.Options"; + public static readonly string ShowAttributesWhenFoldedPropertyName = "ShowAttributesWhenFolded"; + public static readonly string ShowSchemaAnnotationPropertyName = "ShowSchemaAnnotation"; + + static Properties properties; + + static XmlEditorAddInOptions() + { + properties = PropertyService.Get(OptionsProperty, new Properties()); + } + + static Properties Properties { + get { + Debug.Assert(properties != null); + return properties; + } + } + + public static event PropertyChangedEventHandler PropertyChanged { + add { Properties.PropertyChanged += value; } + remove { Properties.PropertyChanged -= value; } + } + + #region Properties + /// + /// Gets an association between a schema and a file extension. + /// + /// + /// The property will be an xml element when the SharpDevelopProperties.xml + /// is read on startup. The property will be a schema association + /// if the user changes the schema associated with the file + /// extension in tools->options. + /// The normal way of doing things is to + /// pass the GetProperty method a default value which auto-magically + /// turns the xml element into a schema association so we would not + /// have to check for both. In this case, however, I do not want + /// a default saved to the SharpDevelopProperties.xml file unless the user + /// makes a change using Tools->Options. + /// If we have a file extension that is currently missing a default + /// schema then if we ship the schema at a later date the association will + /// be updated by the code if the user has not changed the settings themselves. + /// + /// For example, the initial release of the xml editor add-in had + /// no default schema for .xsl files, by default it was associated with + /// no schema and this setting is saved if the user ever viewed the settings + /// in the tools->options dialog. Now, after the initial release the + /// .xsl schema was created and shipped with SharpDevelop, there is + /// no way to associate this schema to .xsl files by default since + /// the property exists in the SharpDevelopProperties.xml file. + /// An alternative way of doing this might be to have the + /// config info in the schema itself, which a special SharpDevelop + /// namespace. I believe this is what Visual Studio does. This + /// way is not as flexible since it requires the user to locate + /// the schema and change the association manually. + /// + public static XmlSchemaAssociation GetSchemaAssociation(string extension) + { + extension = extension.ToLower(); + string property = Properties.Get("ext" + extension, String.Empty); + XmlSchemaAssociation association = null; + + if (property.Length > 0) { + association = XmlSchemaAssociation.ConvertFromString(property); + } + + // Use default? + if (association == null) { + association = XmlSchemaAssociation.GetDefaultAssociation(extension); + } + + return association; + } + + public static void SetSchemaAssociation(XmlSchemaAssociation association) + { + Properties.Set("ext" + association.Extension, association.ConvertToString()); + } + + public static bool ShowAttributesWhenFolded { + get { + return Properties.Get(ShowAttributesWhenFoldedPropertyName, false); + } + + set { + Properties.Set(ShowAttributesWhenFoldedPropertyName, value); + } + } + + public static bool ShowSchemaAnnotation { + get { + return Properties.Get(ShowSchemaAnnotationPropertyName, true); + } + + set { + Properties.Set(ShowSchemaAnnotationPropertyName, value); + } + } + + #endregion + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorOptionsPanel.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorOptionsPanel.cs new file mode 100644 index 0000000000..f0b08fb765 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlEditorOptionsPanel.cs @@ -0,0 +1,49 @@ +// +// +// +// +// $Revision: 3568 $ +// + +using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using System; +using System.Windows.Forms; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Configuration settings for the xml editor. + /// + public class XmlEditorOptionsPanel : XmlFormsOptionPanel + { + static readonly string showAttributesWhenFoldedCheckBoxName = "showAttributesWhenFoldedCheckBox"; + static readonly string showSchemaAnnotationCheckBoxName = "showSchemaAnnotationCheckBox"; + + public XmlEditorOptionsPanel() + { + } + + /// + /// Initialises the panel. + /// + public override void LoadPanelContents() + { + SetupFromXmlStream(this.GetType().Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.XmlEditorOptionsPanel.xfrm")); + + ((CheckBox)ControlDictionary[showAttributesWhenFoldedCheckBoxName]).Checked = XmlEditorAddInOptions.ShowAttributesWhenFolded; + ((CheckBox)ControlDictionary[showSchemaAnnotationCheckBoxName]).Checked = XmlEditorAddInOptions.ShowSchemaAnnotation; + } + + /// + /// Saves any changes. + /// + public override bool StorePanelContents() + { + XmlEditorAddInOptions.ShowAttributesWhenFolded = ((CheckBox)ControlDictionary[showAttributesWhenFoldedCheckBoxName]).Checked; + XmlEditorAddInOptions.ShowSchemaAnnotation = ((CheckBox)ControlDictionary[showSchemaAnnotationCheckBoxName]).Checked; + + return true; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlElementTreeNode.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlElementTreeNode.cs new file mode 100644 index 0000000000..2c932c066c --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlElementTreeNode.cs @@ -0,0 +1,99 @@ +// +// +// +// +// $Revision: 2164 $ +// + +using System; +using System.Xml; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Represents an XmlElement in the Xml Tree. + /// + public class XmlElementTreeNode : ExtTreeNode + { + public const string XmlElementTreeNodeImageKey = "XmlElementTreeNodeImage"; + public const string XmlElementTreeNodeGhostImageKey = "XmlElementTreeNodeGhostImage"; + + XmlElement element; + + public XmlElementTreeNode(XmlElement element) + { + this.element = element; + Text = GetDisplayText(element); + Tag = element; + ImageKey = XmlElementTreeNodeImageKey; + + if (element.HasChildNodes) { + // Add dummy node so that the tree node can be + // expanded in the tree view. + Nodes.Add(new ExtTreeNode()); + } + } + + /// + /// Gets the XmlElement associated with this tree node. + /// + public XmlElement XmlElement { + get { + return element; + } + } + + /// + /// Gets or sets whether to show the ghost image which is + /// displayed when cutting the node. + /// + public bool ShowGhostImage { + get { + return ImageKey == XmlElementTreeNodeGhostImageKey; + } + set { + if (value) { + ImageKey = XmlElementTreeNodeGhostImageKey; + } else { + ImageKey = XmlElementTreeNodeImageKey; + } + SelectedImageKey = ImageKey; + } + } + + /// + /// Adds child elements to this tree node. + /// + protected override void Initialize() + { + Nodes.Clear(); + foreach (XmlNode childNode in element.ChildNodes) { + XmlElement childElement = childNode as XmlElement; + XmlText text = childNode as XmlText; + XmlComment comment = childNode as XmlComment; + if (childElement != null) { + XmlElementTreeNode treeNode = new XmlElementTreeNode(childElement); + treeNode.AddTo(this); + } else if (text != null) { + XmlTextTreeNode treeNode = new XmlTextTreeNode(text); + treeNode.AddTo(this); + } else if (comment != null) { + XmlCommentTreeNode treeNode = new XmlCommentTreeNode(comment); + treeNode.AddTo(this); + } + } + } + + /// + /// Gets the tree node's text for the element. + /// + static string GetDisplayText(XmlElement element) + { + if (element.Prefix.Length > 0) { + return String.Concat(element.Prefix, ":", element.LocalName); + } + return element.LocalName; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociation.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociation.cs new file mode 100644 index 0000000000..cda6484e9b --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociation.cs @@ -0,0 +1,157 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Represents an association between an xml schema and a file extension. + /// + public class XmlSchemaAssociation //: IXmlConvertable + { + string namespaceUri = String.Empty; + string extension = String.Empty; + string namespacePrefix = String.Empty; + + public XmlSchemaAssociation(string extension) + : this(extension, String.Empty, String.Empty) + { + } + + public XmlSchemaAssociation(string extension, string namespaceUri) + : this(extension, namespaceUri, String.Empty) + { + } + + public XmlSchemaAssociation(string extension, string namespaceUri, string namespacePrefix) + { + this.extension = extension; + this.namespaceUri = namespaceUri; + this.namespacePrefix = namespacePrefix; + } + + public string NamespaceUri { + get { + return namespaceUri; + } + + set { + namespaceUri = value; + } + } + + /// + /// Gets or sets the file extension (e.g. '.xml'). + /// + public string Extension { + get { + return extension; + } + + set { + extension = value; + } + } + + /// + /// Gets or sets the default namespace prefix that will be added + /// to the xml elements. + /// + public string NamespacePrefix { + get { + return namespacePrefix; + } + + set { + namespacePrefix = value; + } + } + + /// + /// Gets the default schema association for the file extension. + /// + /// + /// These defaults are hard coded. + /// + public static XmlSchemaAssociation GetDefaultAssociation(string extension) + { + XmlSchemaAssociation association = null; + + switch (extension.ToLowerInvariant()) { + case ".wxs": + association = new XmlSchemaAssociation(extension, @"http://schemas.microsoft.com/wix/2003/01/wi"); + break; + case ".config": + association = new XmlSchemaAssociation(extension, @"urn:app-config"); + break; + case ".build": + association = new XmlSchemaAssociation(extension, @"http://nant.sf.net/release/0.85/nant.xsd"); + break; + case ".addin": + association = new XmlSchemaAssociation(extension, @"http://www.icsharpcode.net/2005/addin"); + break; + case ".xsl": + case ".xslt": + association = new XmlSchemaAssociation(extension, @"http://www.w3.org/1999/XSL/Transform", "xsl"); + break; + case ".xsd": + association = new XmlSchemaAssociation(extension, @"http://www.w3.org/2001/XMLSchema", "xs"); + break; + case ".manifest": + association = new XmlSchemaAssociation(extension, @"urn:schemas-microsoft-com:asm.v1"); + break; + case ".xaml": + association = new XmlSchemaAssociation(extension, @"http://schemas.microsoft.com/winfx/avalon/2005"); + break; + default: + association = new XmlSchemaAssociation(extension); + break; + } + return association; + } + + /// + /// Two schema associations are considered equal if their file extension, + /// prefix and namespaceUri are the same. + /// + public override bool Equals(object obj) + { + bool equals = false; + + XmlSchemaAssociation rhs = obj as XmlSchemaAssociation; + if (rhs != null) { + if ((this.namespacePrefix == rhs.namespacePrefix) && + (this.extension == rhs.extension) && + (this.namespaceUri == rhs.namespaceUri)) { + equals = true; + } + } + + return equals; + } + + public override int GetHashCode() + { + return (namespaceUri != null ? namespaceUri.GetHashCode() : 0) ^ (extension != null ? extension.GetHashCode() : 0) ^ (namespacePrefix != null ? namespacePrefix.GetHashCode() : 0); + } + + /// + /// Creates an XmlSchemaAssociation from the saved xml. + /// + public static XmlSchemaAssociation ConvertFromString(string text) + { + string[] parts = text.Split(new char[] {'|'}, 3); + return new XmlSchemaAssociation(parts[0], parts[1], parts[2]); + } + + public string ConvertToString() + { + return extension + "|" + namespaceUri + "|" + namespacePrefix; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociationListBoxItem.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociationListBoxItem.cs new file mode 100644 index 0000000000..73c61175ff --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaAssociationListBoxItem.cs @@ -0,0 +1,89 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Represents list box item showing the association between an xml schema + /// and a file extension. + /// + public class XmlSchemaAssociationListBoxItem + { + bool isDirty = false; + string namespaceUri = String.Empty; + string extension = String.Empty; + string namespacePrefix = String.Empty; + + public XmlSchemaAssociationListBoxItem(string extension, string namespaceUri, string namespacePrefix) + { + this.extension = extension; + this.namespaceUri = namespaceUri; + this.namespacePrefix = namespacePrefix; + } + + /// + /// Gets or sets whether this association has been changed by the user. + /// + public bool IsDirty { + get { + return isDirty; + } + + set { + isDirty = value; + } + } + + public string NamespaceUri { + get { + return namespaceUri; + } + + set { + namespaceUri = value; + } + } + + /// + /// Gets or sets the file extension (e.g. '.xml'). + /// + public string Extension { + get { + return extension; + } + + set { + extension = value; + } + } + + /// + /// Gets or sets the default namespace prefix that will be added + /// to the xml elements. + /// + public string NamespacePrefix { + get { + return namespacePrefix; + } + + set { + namespacePrefix = value; + } + } + + /// + /// Returns the file extension so this can be sorted in a list box. + /// + /// + public override string ToString() + { + return extension; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionData.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionData.cs new file mode 100644 index 0000000000..4aa2458407 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionData.cs @@ -0,0 +1,1348 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using ICSharpCode.XmlBinding.Parser; +using System; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; + +namespace ICSharpCode.XmlEditor +{ + /* + /// + /// Holds the completion (intellisense) data for an xml schema. + /// + /// + /// The XmlSchema class throws an exception if we attempt to load + /// the xhtml1-strict.xsd schema. It does not like the fact that + /// this schema redefines the xml namespace, even though this is + /// allowed by the w3.org specification. + /// + public class XmlSchemaCompletionData + { + string namespaceUri = String.Empty; + XmlSchema schema; + string fileName = String.Empty; + bool readOnly = false; + + /// + /// Stores attributes that have been prohibited whilst the code + /// generates the attribute completion data. + /// + XmlSchemaObjectCollection prohibitedAttributes = new XmlSchemaObjectCollection(); + + public XmlSchemaCompletionData() + { + } + + /// + /// Creates completion data from the schema passed in + /// via the reader object. + /// + public XmlSchemaCompletionData(TextReader reader) + { + ReadSchema(String.Empty, reader); + } + + /// + /// Creates completion data from the schema passed in + /// via the reader object. + /// + public XmlSchemaCompletionData(XmlTextReader reader) + { + reader.XmlResolver = null; + ReadSchema(reader); + } + + /// + /// Creates the completion data from the specified schema file. + /// + public XmlSchemaCompletionData(string fileName) : this(String.Empty, fileName) + { + } + + /// + /// Creates the completion data from the specified schema file and uses + /// the specified baseUri to resolve any referenced schemas. + /// + public XmlSchemaCompletionData(string baseUri, string fileName) + { + StreamReader reader = new StreamReader(fileName, true); + ReadSchema(baseUri, reader); + this.fileName = fileName; + } + + /// + /// Gets the schema. + /// + public XmlSchema Schema { + get { + return schema; + } + } + + /// + /// Read only schemas are those that are installed with + /// SharpDevelop. + /// + public bool ReadOnly { + get { + return readOnly; + } + + set { + readOnly = value; + } + } + + /// + /// Gets or sets the schema's file name. + /// + public string FileName { + get { + return fileName; + } + set { + fileName = value; + } + } + + /// + /// Gets the namespace URI for the schema. + /// + public string NamespaceUri { + get { + return namespaceUri; + } + } + + /// + /// Converts the filename into a valid Uri. + /// + public static string GetUri(string fileName) + { + string uri = String.Empty; + + if (fileName != null) { + if (fileName.Length > 0) { + uri = String.Concat("file:///", fileName.Replace('\\', '/')); + } + } + + return uri; + } + + /// + /// Gets the possible root elements for an xml document using this schema. + /// + public ICompletionData[] GetElementCompletionData() + { + return GetElementCompletionData(String.Empty); + } + + /// + /// Gets the possible root elements for an xml document using this schema. + /// + public ICompletionData[] GetElementCompletionData(string namespacePrefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (element.Name != null) { + AddElement(data, element.Name, namespacePrefix, element.Annotation); + } else { + // Do not add reference element. + } + } + + return data.ToArray(); + } + + /// + /// Gets the attribute completion data for the xml element that exists + /// at the end of the specified path. + /// + public ICompletionData[] GetAttributeCompletionData(XmlElementPath path) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + // Locate matching element. + XmlSchemaElement element = FindElement(path); + + // Get completion data. + if (element != null) { + prohibitedAttributes.Clear(); + data = GetAttributeCompletionData(element); + } + + return data.ToArray(); + } + + /// + /// Gets the child element completion data for the xml element that exists + /// at the end of the specified path. + /// + public ICompletionData[] GetChildElementCompletionData(XmlElementPath path) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + // Locate matching element. + XmlSchemaElement element = FindElement(path); + + // Get completion data. + if (element != null) { + data = GetChildElementCompletionData(element, path.Elements.LastPrefix); + } + + return data.ToArray(); + } + + /// + /// Gets the autocomplete data for the specified attribute value. + /// + public ICompletionData[] GetAttributeValueCompletionData(XmlElementPath path, string name) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + // Locate matching element. + XmlSchemaElement element = FindElement(path); + + // Get completion data. + if (element != null) { + data = GetAttributeValueCompletionData(element, name); + } + + return data.ToArray(); + } + + /// + /// Finds the element that exists at the specified path. + /// + /// This method is not used when generating completion data, + /// but is a useful method when locating an element so we can jump + /// to its schema definition. + /// if no element can be found. + public XmlSchemaElement FindElement(XmlElementPath path) + { + XmlSchemaElement element = null; + for (int i = 0; i < path.Elements.Count; ++i) { + QualifiedName name = path.Elements[i]; + if (i == 0) { + // Look for root element. + element = FindElement(name); + if (element == null) { + break; + } + } else { + element = FindChildElement(element, name); + if (element == null) { + break; + } + } + } + return element; + } + + /// + /// Finds an element in the schema. + /// + /// + /// Only looks at the elements that are defined in the + /// root of the schema so it will not find any elements + /// that are defined inside any complex types. + /// + public XmlSchemaElement FindElement(QualifiedName name) + { + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (name.Equals(element.QualifiedName)) { + return element; + } + } + return null; + } + + /// + /// Finds the complex type with the specified name. + /// + public XmlSchemaComplexType FindComplexType(QualifiedName name) + { + XmlQualifiedName qualifiedName = new XmlQualifiedName(name.Name, name.Namespace); + return FindNamedType(schema, qualifiedName); + } + + /// + /// Finds the specified attribute name given the element. + /// + /// This method is not used when generating completion data, + /// but is a useful method when locating an attribute so we can jump + /// to its schema definition. + /// if no attribute can be found. + public XmlSchemaAttribute FindAttribute(XmlSchemaElement element, string name) + { + XmlSchemaAttribute attribute = null; + XmlSchemaComplexType complexType = GetElementAsComplexType(element); + if (complexType != null) { + attribute = FindAttribute(complexType, name); + } + return attribute; + } + + /// + /// Finds the attribute group with the specified name. + /// + public XmlSchemaAttributeGroup FindAttributeGroup(string name) + { + return FindAttributeGroup(schema, name); + } + + /// + /// Finds the simple type with the specified name. + /// + public XmlSchemaSimpleType FindSimpleType(string name) + { + XmlQualifiedName qualifiedName = new XmlQualifiedName(name, namespaceUri); + return FindSimpleType(qualifiedName); + } + + /// + /// Finds the specified attribute in the schema. This method only checks + /// the attributes defined in the root of the schema. + /// + public XmlSchemaAttribute FindAttribute(string name) + { + foreach (XmlSchemaAttribute attribute in schema.Attributes.Values) { + if (attribute.Name == name) { + return attribute; + } + } + return null; + } + + /// + /// Finds the schema group with the specified name. + /// + public XmlSchemaGroup FindGroup(string name) + { + if (name != null) { + foreach (XmlSchemaObject schemaObject in schema.Groups.Values) { + XmlSchemaGroup group = schemaObject as XmlSchemaGroup; + if (group != null) { + if (group.Name == name) { + return group; + } + } + } + } + return null; + } + + /// + /// Takes the name and creates a qualified name using the namespace of this + /// schema. + /// + /// If the name is of the form myprefix:mytype then the correct + /// namespace is determined from the prefix. If the name is not of this + /// form then no prefix is added. + public QualifiedName CreateQualifiedName(string name) + { + int index = name.IndexOf(":"); + if (index >= 0) { + string prefix = name.Substring(0, index); + name = name.Substring(index + 1); + foreach (XmlQualifiedName xmlQualifiedName in schema.Namespaces.ToArray()) { + if (xmlQualifiedName.Name == prefix) { + return new QualifiedName(name, xmlQualifiedName.Namespace, prefix); + } + } + } + + // Default behaviour just return the name with the namespace uri. + return new QualifiedName(name, namespaceUri); + } + + /// + /// Converts the element to a complex type if possible. + /// + public XmlSchemaComplexType GetElementAsComplexType(XmlSchemaElement element) + { + XmlSchemaComplexType complexType = element.SchemaType as XmlSchemaComplexType; + if (complexType == null) { + complexType = FindNamedType(schema, element.SchemaTypeName); + } + return complexType; + } + + /// + /// Handler for schema validation errors. + /// + void SchemaValidation(object source, ValidationEventArgs e) + { + // Do nothing. + } + + /// + /// Loads the schema. + /// + void ReadSchema(XmlReader reader) + { + try { + schema = XmlSchema.Read(reader, new ValidationEventHandler(SchemaValidation)); + schema.Compile(new ValidationEventHandler(SchemaValidation)); + + namespaceUri = schema.TargetNamespace; + } finally { + reader.Close(); + } + } + + void ReadSchema(string baseUri, TextReader reader) + { + XmlTextReader xmlReader = new XmlTextReader(baseUri, reader); + + // Setting the resolver to null allows us to + // load the xhtml1-strict.xsd without any exceptions if + // the referenced dtds exist in the same folder as the .xsd + // file. If this is not set to null the dtd files are looked + // for in the assembly's folder. + xmlReader.XmlResolver = null; + ReadSchema(xmlReader); + } + + /// + /// Finds an element in the schema. + /// + /// + /// Only looks at the elements that are defined in the + /// root of the schema so it will not find any elements + /// that are defined inside any complex types. + /// + XmlSchemaElement FindElement(XmlQualifiedName name) + { + XmlSchemaElement matchedElement = null; + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (name.Equals(element.QualifiedName)) { + matchedElement = element; + break; + } + } + + return matchedElement; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaElement element, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaComplexType complexType = GetElementAsComplexType(element); + + if (complexType != null) { + data = GetChildElementCompletionData(complexType, prefix); + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaComplexType complexType, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaSequence sequence = complexType.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = complexType.Particle as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = complexType.Particle as XmlSchemaGroupRef; + XmlSchemaComplexContent complexContent = complexType.ContentModel as XmlSchemaComplexContent; + XmlSchemaAll all = complexType.Particle as XmlSchemaAll; + + if (sequence != null) { + data = GetChildElementCompletionData(sequence.Items, prefix); + } else if (choice != null) { + data = GetChildElementCompletionData(choice.Items, prefix); + } else if (complexContent != null) { + data = GetChildElementCompletionData(complexContent, prefix); + } else if (groupRef != null) { + data = GetChildElementCompletionData(groupRef, prefix); + } else if (all != null) { + data = GetChildElementCompletionData(all.Items, prefix); + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaObjectCollection items, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + foreach (XmlSchemaObject schemaObject in items) { + + XmlSchemaElement childElement = schemaObject as XmlSchemaElement; + XmlSchemaSequence childSequence = schemaObject as XmlSchemaSequence; + XmlSchemaChoice childChoice = schemaObject as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = schemaObject as XmlSchemaGroupRef; + + if (childElement != null) { + string name = childElement.Name; + if (name == null) { + name = childElement.RefName.Name; + XmlSchemaElement element = FindElement(childElement.RefName); + if (element != null) { + if (element.IsAbstract) { + AddSubstitionGroupElements(data, element.QualifiedName, prefix); + } else { + AddElement(data, name, prefix, element.Annotation); + } + } else { + AddElement(data, name, prefix, childElement.Annotation); + } + } else { + AddElement(data, name, prefix, childElement.Annotation); + } + } else if (childSequence != null) { + AddElements(data, GetChildElementCompletionData(childSequence.Items, prefix)); + } else if (childChoice != null) { + AddElements(data, GetChildElementCompletionData(childChoice.Items, prefix)); + } else if (groupRef != null) { + AddElements(data, GetChildElementCompletionData(groupRef, prefix)); + } + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaComplexContent complexContent, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaComplexContentExtension extension = complexContent.Content as XmlSchemaComplexContentExtension; + if (extension != null) { + data = GetChildElementCompletionData(extension, prefix); + } else { + XmlSchemaComplexContentRestriction restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (restriction != null) { + data = GetChildElementCompletionData(restriction, prefix); + } + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaComplexContentExtension extension, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaComplexType complexType = FindNamedType(schema, extension.BaseTypeName); + if (complexType != null) { + data = GetChildElementCompletionData(complexType, prefix); + } + + // Add any elements. + if (extension.Particle != null) { + XmlSchemaSequence sequence = extension.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = extension.Particle as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = extension.Particle as XmlSchemaGroupRef; + + if(sequence != null) { + data.AddRange(GetChildElementCompletionData(sequence.Items, prefix)); + } else if (choice != null) { + data.AddRange(GetChildElementCompletionData(choice.Items, prefix)); + } else if (groupRef != null) { + data.AddRange(GetChildElementCompletionData(groupRef, prefix)); + } + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaGroupRef groupRef, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaGroup group = FindGroup(groupRef.RefName.Name); + if (group != null) { + XmlSchemaSequence sequence = group.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = group.Particle as XmlSchemaChoice; + + if(sequence != null) { + data = GetChildElementCompletionData(sequence.Items, prefix); + } else if (choice != null) { + data = GetChildElementCompletionData(choice.Items, prefix); + } + } + + return data; + } + + XmlCompletionDataCollection GetChildElementCompletionData(XmlSchemaComplexContentRestriction restriction, string prefix) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + // Add any elements. + if (restriction.Particle != null) { + XmlSchemaSequence sequence = restriction.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = restriction.Particle as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = restriction.Particle as XmlSchemaGroupRef; + + if(sequence != null) { + data = GetChildElementCompletionData(sequence.Items, prefix); + } else if (choice != null) { + data = GetChildElementCompletionData(choice.Items, prefix); + } else if (groupRef != null) { + data = GetChildElementCompletionData(groupRef, prefix); + } + } + + return data; + } + + /// + /// Adds an element completion data to the collection if it does not + /// already exist. + /// + void AddElement(XmlCompletionDataCollection data, string name, string prefix, string documentation) + { + if (!data.Contains(name)) { + if (prefix.Length > 0) { + name = String.Concat(prefix, ":", name); + } + XmlCompletionData completionData = new XmlCompletionData(name, documentation); + data.Add(completionData); + } + } + + /// + /// Adds an element completion data to the collection if it does not + /// already exist. + /// + void AddElement(XmlCompletionDataCollection data, string name, string prefix, XmlSchemaAnnotation annotation) + { + // Get any annotation documentation. + string documentation = GetDocumentation(annotation); + + AddElement(data, name, prefix, documentation); + } + + /// + /// Adds elements to the collection if it does not already exist. + /// + void AddElements(XmlCompletionDataCollection lhs, XmlCompletionDataCollection rhs) + { + foreach (XmlCompletionData data in rhs) { + if (!lhs.Contains(data)) { + lhs.Add(data); + } + } + } + + /// + /// Gets the documentation from the annotation element. + /// + /// + /// All documentation elements are added. All text nodes inside + /// the documentation element are added. + /// + string GetDocumentation(XmlSchemaAnnotation annotation) + { + string documentation = String.Empty; + + if (annotation != null) { + StringBuilder documentationBuilder = new StringBuilder(); + foreach (XmlSchemaObject schemaObject in annotation.Items) { + XmlSchemaDocumentation schemaDocumentation = schemaObject as XmlSchemaDocumentation; + if (schemaDocumentation != null) { + foreach (XmlNode node in schemaDocumentation.Markup) { + XmlText textNode = node as XmlText; + if (textNode != null) { + if (textNode.Data != null) { + if (textNode.Data.Length > 0) { + documentationBuilder.Append(textNode.Data); + } + } + } + } + } + } + + documentation = documentationBuilder.ToString(); + } + + return documentation; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaElement element) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaComplexType complexType = GetElementAsComplexType(element); + + if (complexType != null) { + data.AddRange(GetAttributeCompletionData(complexType)); + } + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaComplexContentRestriction restriction) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + data.AddRange(GetAttributeCompletionData(restriction.Attributes)); + + XmlSchemaComplexType baseComplexType = FindNamedType(schema, restriction.BaseTypeName); + if (baseComplexType != null) { + data.AddRange(GetAttributeCompletionData(baseComplexType)); + } + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaComplexType complexType) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + data = GetAttributeCompletionData(complexType.Attributes); + + // Add any complex content attributes. + XmlSchemaComplexContent complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) { + XmlSchemaComplexContentExtension extension = complexContent.Content as XmlSchemaComplexContentExtension; + XmlSchemaComplexContentRestriction restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (extension != null) { + data.AddRange(GetAttributeCompletionData(extension)); + } else if (restriction != null) { + data.AddRange(GetAttributeCompletionData(restriction)); + } + } else { + XmlSchemaSimpleContent simpleContent = complexType.ContentModel as XmlSchemaSimpleContent; + if (simpleContent != null) { + data.AddRange(GetAttributeCompletionData(simpleContent)); + } + } + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaComplexContentExtension extension) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + data.AddRange(GetAttributeCompletionData(extension.Attributes)); + XmlSchemaComplexType baseComplexType = FindNamedType(schema, extension.BaseTypeName); + if (baseComplexType != null) { + data.AddRange(GetAttributeCompletionData(baseComplexType)); + } + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaSimpleContent simpleContent) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaSimpleContentExtension extension = simpleContent.Content as XmlSchemaSimpleContentExtension; + if (extension != null) { + data.AddRange(GetAttributeCompletionData(extension)); + } + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaSimpleContentExtension extension) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + data.AddRange(GetAttributeCompletionData(extension.Attributes)); + + return data; + } + + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaObjectCollection attributes) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + foreach (XmlSchemaObject schemaObject in attributes) { + XmlSchemaAttribute attribute = schemaObject as XmlSchemaAttribute; + XmlSchemaAttributeGroupRef attributeGroupRef = schemaObject as XmlSchemaAttributeGroupRef; + if (attribute != null) { + if (!IsProhibitedAttribute(attribute)) { + AddAttribute(data, attribute); + } else { + prohibitedAttributes.Add(attribute); + } + } else if (attributeGroupRef != null) { + data.AddRange(GetAttributeCompletionData(attributeGroupRef)); + } + } + return data; + } + + /// + /// Checks that the attribute is prohibited or has been flagged + /// as prohibited previously. + /// + bool IsProhibitedAttribute(XmlSchemaAttribute attribute) + { + bool prohibited = false; + if (attribute.Use == XmlSchemaUse.Prohibited) { + prohibited = true; + } else { + foreach (XmlSchemaAttribute prohibitedAttribute in prohibitedAttributes) { + if (prohibitedAttribute.QualifiedName == attribute.QualifiedName) { + prohibited = true; + break; + } + } + } + + return prohibited; + } + + /// + /// Adds an attribute to the completion data collection. + /// + /// + /// Note the special handling of xml:lang attributes. + /// + void AddAttribute(XmlCompletionDataCollection data, XmlSchemaAttribute attribute) + { + string name = attribute.Name; + if (name == null) { + if (attribute.RefName.Namespace == "http://www.w3.org/XML/1998/namespace") { + name = String.Concat("xml:", attribute.RefName.Name); + } + } + + if (name != null) { + string documentation = GetDocumentation(attribute.Annotation); + XmlCompletionData completionData = new XmlCompletionData(name, documentation, XmlCompletionData.DataType.XmlAttribute); + data.Add(completionData); + } + } + + /// + /// Gets attribute completion data from a group ref. + /// + XmlCompletionDataCollection GetAttributeCompletionData(XmlSchemaAttributeGroupRef groupRef) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + XmlSchemaAttributeGroup group = FindAttributeGroup(schema, groupRef.RefName.Name); + if (group != null) { + data = GetAttributeCompletionData(group.Attributes); + } + + return data; + } + + static XmlSchemaComplexType FindNamedType(XmlSchema schema, XmlQualifiedName name) + { + XmlSchemaComplexType matchedComplexType = null; + + if (name != null) { + foreach (XmlSchemaObject schemaObject in schema.Items) { + XmlSchemaComplexType complexType = schemaObject as XmlSchemaComplexType; + if (complexType != null) { + if (complexType.QualifiedName == name) { + matchedComplexType = complexType; + break; + } + } + } + + // Try included schemas. + if (matchedComplexType == null) { + foreach (XmlSchemaExternal external in schema.Includes) { + XmlSchemaInclude include = external as XmlSchemaInclude; + if (include != null) { + if (include.Schema != null) { + matchedComplexType = FindNamedType(include.Schema, name); + } + } + } + } + } + + return matchedComplexType; + } + + /// + /// Finds an element that matches the specified + /// from the children of the given . + /// + XmlSchemaElement FindChildElement(XmlSchemaElement element, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + XmlSchemaComplexType complexType = GetElementAsComplexType(element); + if (complexType != null) { + matchedElement = FindChildElement(complexType, name); + } + + return matchedElement; + } + + XmlSchemaElement FindChildElement(XmlSchemaComplexType complexType, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + XmlSchemaSequence sequence = complexType.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = complexType.Particle as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = complexType.Particle as XmlSchemaGroupRef; + XmlSchemaAll all = complexType.Particle as XmlSchemaAll; + XmlSchemaComplexContent complexContent = complexType.ContentModel as XmlSchemaComplexContent; + + if (sequence != null) { + matchedElement = FindElement(sequence.Items, name); + } else if (choice != null) { + matchedElement = FindElement(choice.Items, name); + } else if (complexContent != null) { + XmlSchemaComplexContentExtension extension = complexContent.Content as XmlSchemaComplexContentExtension; + XmlSchemaComplexContentRestriction restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + if (extension != null) { + matchedElement = FindChildElement(extension, name); + } else if (restriction != null) { + matchedElement = FindChildElement(restriction, name); + } + } else if (groupRef != null) { + matchedElement = FindElement(groupRef, name); + } else if (all != null) { + matchedElement = FindElement(all.Items, name); + } + + return matchedElement; + } + + /// + /// Finds the named child element contained in the extension element. + /// + XmlSchemaElement FindChildElement(XmlSchemaComplexContentExtension extension, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + XmlSchemaComplexType complexType = FindNamedType(schema, extension.BaseTypeName); + if (complexType != null) { + matchedElement = FindChildElement(complexType, name); + + if (matchedElement == null) { + + XmlSchemaSequence sequence = extension.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = extension.Particle as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = extension.Particle as XmlSchemaGroupRef; + + if (sequence != null) { + matchedElement = FindElement(sequence.Items, name); + } else if (choice != null) { + matchedElement = FindElement(choice.Items, name); + } else if (groupRef != null) { + matchedElement = FindElement(groupRef, name); + } + } + } + + return matchedElement; + } + + /// + /// Finds the named child element contained in the restriction element. + /// + XmlSchemaElement FindChildElement(XmlSchemaComplexContentRestriction restriction, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + XmlSchemaSequence sequence = restriction.Particle as XmlSchemaSequence; + XmlSchemaGroupRef groupRef = restriction.Particle as XmlSchemaGroupRef; + + if (sequence != null) { + matchedElement = FindElement(sequence.Items, name); + } else if (groupRef != null) { + matchedElement = FindElement(groupRef, name); + } + + return matchedElement; + } + + /// + /// Finds the element in the collection of schema objects. + /// + XmlSchemaElement FindElement(XmlSchemaObjectCollection items, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + foreach (XmlSchemaObject schemaObject in items) { + XmlSchemaElement element = schemaObject as XmlSchemaElement; + XmlSchemaSequence sequence = schemaObject as XmlSchemaSequence; + XmlSchemaChoice choice = schemaObject as XmlSchemaChoice; + XmlSchemaGroupRef groupRef = schemaObject as XmlSchemaGroupRef; + + if (element != null) { + if (element.Name != null) { + if (name.Name == element.Name) { + matchedElement = element; + } + } else if (element.RefName != null) { + if (name.Name == element.RefName.Name) { + matchedElement = FindElement(element.RefName); + } else { + // Abstract element? + XmlSchemaElement abstractElement = FindElement(element.RefName); + if (abstractElement != null && abstractElement.IsAbstract) { + matchedElement = FindSubstitutionGroupElement(abstractElement.QualifiedName, name); + } + } + } + } else if (sequence != null) { + matchedElement = FindElement(sequence.Items, name); + } else if (choice != null) { + matchedElement = FindElement(choice.Items, name); + } else if (groupRef != null) { + matchedElement = FindElement(groupRef, name); + } + + // Did we find a match? + if (matchedElement != null) { + break; + } + } + + return matchedElement; + } + + XmlSchemaElement FindElement(XmlSchemaGroupRef groupRef, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + XmlSchemaGroup group = FindGroup(groupRef.RefName.Name); + if (group != null) { + XmlSchemaSequence sequence = group.Particle as XmlSchemaSequence; + XmlSchemaChoice choice = group.Particle as XmlSchemaChoice; + + if(sequence != null) { + matchedElement = FindElement(sequence.Items, name); + } else if (choice != null) { + matchedElement = FindElement(choice.Items, name); + } + } + + return matchedElement; + } + + static XmlSchemaAttributeGroup FindAttributeGroup(XmlSchema schema, string name) + { + XmlSchemaAttributeGroup matchedGroup = null; + + if (name != null) { + foreach (XmlSchemaObject schemaObject in schema.Items) { + + XmlSchemaAttributeGroup group = schemaObject as XmlSchemaAttributeGroup; + if (group != null) { + if (group.Name == name) { + matchedGroup = group; + break; + } + } + } + + // Try included schemas. + if (matchedGroup == null) { + foreach (XmlSchemaExternal external in schema.Includes) { + XmlSchemaInclude include = external as XmlSchemaInclude; + if (include != null) { + if (include.Schema != null) { + matchedGroup = FindAttributeGroup(include.Schema, name); + } + } + } + } + } + + return matchedGroup; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaElement element, string name) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaComplexType complexType = GetElementAsComplexType(element); + if (complexType != null) { + XmlSchemaAttribute attribute = FindAttribute(complexType, name); + if (attribute != null) { + data.AddRange(GetAttributeValueCompletionData(attribute)); + } + } + + return data; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaAttribute attribute) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + if (attribute.SchemaType != null) { + XmlSchemaSimpleTypeRestriction simpleTypeRestriction = attribute.SchemaType.Content as XmlSchemaSimpleTypeRestriction; + if (simpleTypeRestriction != null) { + data.AddRange(GetAttributeValueCompletionData(simpleTypeRestriction)); + } + } else if (attribute.AttributeSchemaType != null) { + XmlSchemaSimpleType simpleType = attribute.AttributeSchemaType as XmlSchemaSimpleType; + + if (simpleType != null) { + if (simpleType.Name == "boolean") { + data.AddRange(GetBooleanAttributeValueCompletionData()); + } else { + data.AddRange(GetAttributeValueCompletionData(simpleType)); + } + } + } + + return data; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaSimpleTypeRestriction simpleTypeRestriction) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + foreach (XmlSchemaObject schemaObject in simpleTypeRestriction.Facets) { + XmlSchemaEnumerationFacet enumFacet = schemaObject as XmlSchemaEnumerationFacet; + if (enumFacet != null) { + AddAttributeValue(data, enumFacet.Value, enumFacet.Annotation); + } + } + + return data; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaSimpleTypeUnion union) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + foreach (XmlSchemaObject schemaObject in union.BaseTypes) { + XmlSchemaSimpleType simpleType = schemaObject as XmlSchemaSimpleType; + if (simpleType != null) { + data.AddRange(GetAttributeValueCompletionData(simpleType)); + } + } + + return data; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaSimpleType simpleType) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + XmlSchemaSimpleTypeRestriction simpleTypeRestriction = simpleType.Content as XmlSchemaSimpleTypeRestriction; + XmlSchemaSimpleTypeUnion union = simpleType.Content as XmlSchemaSimpleTypeUnion; + XmlSchemaSimpleTypeList list = simpleType.Content as XmlSchemaSimpleTypeList; + + if (simpleTypeRestriction != null) { + data.AddRange(GetAttributeValueCompletionData(simpleTypeRestriction)); + } else if (union != null) { + data.AddRange(GetAttributeValueCompletionData(union)); + } else if (list != null) { + data.AddRange(GetAttributeValueCompletionData(list)); + } + + return data; + } + + XmlCompletionDataCollection GetAttributeValueCompletionData(XmlSchemaSimpleTypeList list) + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + if (list.ItemType != null) { + data.AddRange(GetAttributeValueCompletionData(list.ItemType)); + } else if (list.ItemTypeName != null) { + XmlSchemaSimpleType simpleType = FindSimpleType(list.ItemTypeName); + if (simpleType != null) { + data.AddRange(GetAttributeValueCompletionData(simpleType)); + } + } + + return data; + } + + /// + /// Gets the set of attribute values for an xs:boolean type. + /// + XmlCompletionDataCollection GetBooleanAttributeValueCompletionData() + { + XmlCompletionDataCollection data = new XmlCompletionDataCollection(); + + AddAttributeValue(data, "0"); + AddAttributeValue(data, "1"); + AddAttributeValue(data, "true"); + AddAttributeValue(data, "false"); + + return data; + } + + XmlSchemaAttribute FindAttribute(XmlSchemaComplexType complexType, string name) + { + XmlSchemaAttribute matchedAttribute = null; + + matchedAttribute = FindAttribute(complexType.Attributes, name); + + if (matchedAttribute == null) { + XmlSchemaComplexContent complexContent = complexType.ContentModel as XmlSchemaComplexContent; + if (complexContent != null) { + matchedAttribute = FindAttribute(complexContent, name); + } + } + + return matchedAttribute; + } + + XmlSchemaAttribute FindAttribute(XmlSchemaObjectCollection schemaObjects, string name) + { + XmlSchemaAttribute matchedAttribute = null; + + foreach (XmlSchemaObject schemaObject in schemaObjects) { + XmlSchemaAttribute attribute = schemaObject as XmlSchemaAttribute; + XmlSchemaAttributeGroupRef groupRef = schemaObject as XmlSchemaAttributeGroupRef; + + if (attribute != null) { + if (attribute.Name == name) { + matchedAttribute = attribute; + break; + } + } else if (groupRef != null) { + matchedAttribute = FindAttribute(groupRef, name); + if (matchedAttribute != null) { + break; + } + } + } + + return matchedAttribute; + } + + XmlSchemaAttribute FindAttribute(XmlSchemaAttributeGroupRef groupRef, string name) + { + XmlSchemaAttribute matchedAttribute = null; + + if (groupRef.RefName != null) { + XmlSchemaAttributeGroup group = FindAttributeGroup(schema, groupRef.RefName.Name); + if (group != null) { + matchedAttribute = FindAttribute(group.Attributes, name); + } + } + + return matchedAttribute; + } + + XmlSchemaAttribute FindAttribute(XmlSchemaComplexContent complexContent, string name) + { + XmlSchemaAttribute matchedAttribute = null; + + XmlSchemaComplexContentExtension extension = complexContent.Content as XmlSchemaComplexContentExtension; + XmlSchemaComplexContentRestriction restriction = complexContent.Content as XmlSchemaComplexContentRestriction; + + if (extension != null) { + matchedAttribute = FindAttribute(extension, name); + } else if (restriction != null) { + matchedAttribute = FindAttribute(restriction, name); + } + + return matchedAttribute; + } + + XmlSchemaAttribute FindAttribute(XmlSchemaComplexContentExtension extension, string name) + { + return FindAttribute(extension.Attributes, name); + } + + XmlSchemaAttribute FindAttribute(XmlSchemaComplexContentRestriction restriction, string name) + { + XmlSchemaAttribute matchedAttribute = FindAttribute(restriction.Attributes, name); + + if (matchedAttribute == null) { + XmlSchemaComplexType complexType = FindNamedType(schema, restriction.BaseTypeName); + if (complexType != null) { + matchedAttribute = FindAttribute(complexType, name); + } + } + + return matchedAttribute; + } + + /// + /// Adds an attribute value to the completion data collection. + /// + void AddAttributeValue(XmlCompletionDataCollection data, string valueText) + { + XmlCompletionData completionData = new XmlCompletionData(valueText, XmlCompletionData.DataType.XmlAttributeValue); + data.Add(completionData); + } + + /// + /// Adds an attribute value to the completion data collection. + /// + void AddAttributeValue(XmlCompletionDataCollection data, string valueText, XmlSchemaAnnotation annotation) + { + string documentation = GetDocumentation(annotation); + XmlCompletionData completionData = new XmlCompletionData(valueText, documentation, XmlCompletionData.DataType.XmlAttributeValue); + data.Add(completionData); + } + + /// + /// Adds an attribute value to the completion data collection. + /// + void AddAttributeValue(XmlCompletionDataCollection data, string valueText, string description) + { + XmlCompletionData completionData = new XmlCompletionData(valueText, description, XmlCompletionData.DataType.XmlAttributeValue); + data.Add(completionData); + } + + XmlSchemaSimpleType FindSimpleType(XmlQualifiedName name) + { + XmlSchemaSimpleType matchedSimpleType = null; + + foreach (XmlSchemaObject schemaObject in schema.SchemaTypes.Values) { + XmlSchemaSimpleType simpleType = schemaObject as XmlSchemaSimpleType; + if (simpleType != null) { + if (simpleType.QualifiedName == name) { + matchedSimpleType = simpleType; + break; + } + } + } + + return matchedSimpleType; + } + + /// + /// Adds any elements that have the specified substitution group. + /// + void AddSubstitionGroupElements(XmlCompletionDataCollection data, XmlQualifiedName group, string prefix) + { + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (element.SubstitutionGroup == group) { + AddElement(data, element.Name, prefix, element.Annotation); + } + } + } + + /// + /// Looks for the substitution group element of the specified name. + /// + XmlSchemaElement FindSubstitutionGroupElement(XmlQualifiedName group, QualifiedName name) + { + XmlSchemaElement matchedElement = null; + + foreach (XmlSchemaElement element in schema.Elements.Values) { + if (element.SubstitutionGroup == group) { + if (element.Name != null) { + if (element.Name == name.Name) { + matchedElement = element; + break; + } + } + } + } + + return matchedElement; + } + } + */ +} + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionDataCollection.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionDataCollection.cs new file mode 100644 index 0000000000..6867d8e140 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaCompletionDataCollection.cs @@ -0,0 +1,292 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.Collections.Generic; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /* + /// + /// A collection that stores objects. + /// + [Serializable()] + public class XmlSchemaCompletionDataCollection : System.Collections.CollectionBase { + + /// + /// Initializes a new instance of . + /// + public XmlSchemaCompletionDataCollection() + { + } + + /// + /// Initializes a new instance of based on another . + /// + /// + /// A from which the contents are copied + /// + public XmlSchemaCompletionDataCollection(XmlSchemaCompletionDataCollection val) + { + this.AddRange(val); + } + + /// + /// Initializes a new instance of containing any array of objects. + /// + /// + /// A array of objects with which to intialize the collection + /// + public XmlSchemaCompletionDataCollection(XmlSchemaCompletionData[] val) + { + this.AddRange(val); + } + + /// + /// Represents the entry at the specified index of the . + /// + /// The zero-based index of the entry to locate in the collection. + /// The entry at the specified index of the collection. + /// is outside the valid range of indexes for the collection. + public XmlSchemaCompletionData this[int index] { + get { + return ((XmlSchemaCompletionData)(List[index])); + } + set { + List[index] = value; + } + } + + public ICompletionData[] GetNamespaceCompletionData() + { + List completionItems = new List(); + + foreach (XmlSchemaCompletionData schema in this) { + XmlCompletionData completionData = new XmlCompletionData(schema.NamespaceUri, XmlCompletionData.DataType.NamespaceUri); + completionItems.Add(completionData); + } + + return completionItems.ToArray(); + } + + /// + /// Represents the entry with the specified namespace URI. + /// + /// The schema's namespace URI. + /// The entry with the specified namespace URI. + public XmlSchemaCompletionData this[string namespaceUri] { + get { + return GetItem(namespaceUri); + } + } + + /// + /// Adds a with the specified value to the + /// . + /// + /// The to add. + /// The index at which the new element was inserted. + /// + public int Add(XmlSchemaCompletionData val) + { + return List.Add(val); + } + + /// + /// Copies the elements of an array to the end of the . + /// + /// + /// An array of type containing the objects to add to the collection. + /// + /// + public void AddRange(XmlSchemaCompletionData[] val) + { + for (int i = 0; i < val.Length; i++) { + this.Add(val[i]); + } + } + + /// + /// Adds the contents of another to the end of the collection. + /// + /// + /// A containing the objects to add to the collection. + /// + /// + public void AddRange(XmlSchemaCompletionDataCollection val) + { + for (int i = 0; i < val.Count; i++) + { + this.Add(val[i]); + } + } + + /// + /// Gets a value indicating whether the + /// contains the specified . + /// + /// The to locate. + /// + /// if the is contained in the collection; + /// otherwise, . + /// + /// + public bool Contains(XmlSchemaCompletionData val) + { + return List.Contains(val); + } + + /// + /// Copies the values to a one-dimensional instance at the + /// specified index. + /// + /// The one-dimensional that is the destination of the values copied from . + /// The index in where copying begins. + /// + /// is multidimensional. + /// -or- + /// The number of elements in the is greater than + /// the available space between and the end of + /// . + /// + /// is . + /// is less than 's lowbound. + /// + public void CopyTo(XmlSchemaCompletionData[] array, int index) + { + List.CopyTo(array, index); + } + + /// + /// Returns the index of a in + /// the . + /// + /// The to locate. + /// + /// The index of the of in the + /// , if found; otherwise, -1. + /// + /// + public int IndexOf(XmlSchemaCompletionData val) + { + return List.IndexOf(val); + } + + /// + /// Inserts a into the at the specified index. + /// + /// The zero-based index where should be inserted. + /// The to insert. + /// + public void Insert(int index, XmlSchemaCompletionData val) + { + List.Insert(index, val); + } + + /// + /// Returns an enumerator that can iterate through the . + /// + /// + public new XmlSchemaCompletionDataEnumerator GetEnumerator() + { + return new XmlSchemaCompletionDataEnumerator(this); + } + + /// + /// Removes a specific from the . + /// + /// The to remove from the . + /// is not found in the Collection. + public void Remove(XmlSchemaCompletionData val) + { + List.Remove(val); + } + + /// + /// Gets the schema completion data with the same filename. + /// + /// if no matching schema found. + public XmlSchemaCompletionData GetSchemaFromFileName(string fileName) + { + foreach (XmlSchemaCompletionData schema in this) { + if (FileUtility.IsEqualFileName(schema.FileName, fileName)) { + return schema; + } + } + return null; + } + + /// + /// Enumerator that can iterate through a XmlSchemaCompletionDataCollection. + /// + /// + /// + /// + public class XmlSchemaCompletionDataEnumerator : System.Collections.IEnumerator + { + System.Collections.IEnumerator baseEnumerator; + System.Collections.IEnumerable temp; + + /// + /// Initializes a new instance of . + /// + public XmlSchemaCompletionDataEnumerator(XmlSchemaCompletionDataCollection mappings) + { + this.temp = ((System.Collections.IEnumerable)(mappings)); + this.baseEnumerator = temp.GetEnumerator(); + } + + /// + /// Gets the current in the . + /// + public XmlSchemaCompletionData Current { + get { + return ((XmlSchemaCompletionData)(baseEnumerator.Current)); + } + } + + object System.Collections.IEnumerator.Current { + get { + return baseEnumerator.Current; + } + } + + /// + /// Advances the enumerator to the next of the . + /// + public bool MoveNext() + { + return baseEnumerator.MoveNext(); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the . + /// + public void Reset() + { + baseEnumerator.Reset(); + } + } + + XmlSchemaCompletionData GetItem(string namespaceUri) + { + XmlSchemaCompletionData matchedItem = null; + + foreach(XmlSchemaCompletionData item in this) + { + if (item.NamespaceUri == namespaceUri) { + matchedItem = item; + break; + } + } + + return matchedItem; + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaListBoxItem.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaListBoxItem.cs new file mode 100644 index 0000000000..45a7326bbb --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaListBoxItem.cs @@ -0,0 +1,59 @@ +// +// +// +// +// $Revision: 915 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// A schema item shown in the tools options dialog. + /// + public class XmlSchemaListBoxItem + { + string namespaceUri = String.Empty; + bool readOnly = false; + + /// + /// Creates a new list box item. + /// + /// + /// A readonly list box item is used for system schemas, those that + /// are installed with SharpDevelop. + /// + public XmlSchemaListBoxItem(string namespaceUri, bool readOnly) + { + this.namespaceUri = namespaceUri; + this.readOnly = readOnly; + } + + public XmlSchemaListBoxItem(string namespaceUri) + : this(namespaceUri, false) + { + } + + public string NamespaceUri { + get { + return namespaceUri; + } + } + + public bool ReadOnly { + get { + return readOnly; + } + } + + /// + /// Returns the namespace Uri so the list box item is sorted correctly. + /// + /// + public override string ToString() + { + return namespaceUri; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaManager.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaManager.cs new file mode 100644 index 0000000000..730ecf8346 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemaManager.cs @@ -0,0 +1,223 @@ +// +// +// +// +// $Revision: 1965 $ +// + +using System; +using System.IO; +using System.Runtime.InteropServices; + +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ +/* + /// + /// Keeps track of all the schemas that the Xml Editor is aware + /// of. + /// + public class XmlSchemaManager + { + public const string XmlSchemaNamespace = "http://www.w3.org/2001/XMLSchema"; + + static XmlSchemaCompletionDataCollection schemas = null; + static XmlSchemaManager manager = null; + + public static event EventHandler UserSchemaAdded; + + public static event EventHandler UserSchemaRemoved; + + XmlSchemaManager() + { + } + + /// + /// Determines whether the specified namespace is actually the W3C namespace for + /// XSD files. + /// + public static bool IsXmlSchemaNamespace(string schemaNamespace) + { + return schemaNamespace == XmlSchemaNamespace; + } + + /// + /// Gets the schemas that SharpDevelop knows about. + /// + public static XmlSchemaCompletionDataCollection SchemaCompletionDataItems { + get { + if (schemas == null) { + schemas = new XmlSchemaCompletionDataCollection(); + manager = new XmlSchemaManager(); + manager.ReadSchemas(); + } + + return schemas; + } + } + + /// + /// Gets the schema completion data that is associated with the + /// specified file extension. + /// + public static XmlSchemaCompletionData GetSchemaCompletionData(string extension) + { + XmlSchemaCompletionData data = null; + + XmlSchemaAssociation association = XmlEditorAddInOptions.GetSchemaAssociation(extension); + if (association != null) { + if (association.NamespaceUri.Length > 0) { + data = SchemaCompletionDataItems[association.NamespaceUri]; + } + } + return data; + } + + /// + /// Gets the namespace prefix that is associated with the + /// specified file extension. + /// + public static string GetNamespacePrefix(string extension) + { + string prefix = String.Empty; + + XmlSchemaAssociation association = XmlEditorAddInOptions.GetSchemaAssociation(extension); + if (association != null) { + prefix = association.NamespacePrefix; + } + + return prefix; + } + + /// + /// Removes the schema with the specified namespace from the + /// user schemas folder and removes the completion data. + /// + public static void RemoveUserSchema(string namespaceUri) + { + XmlSchemaCompletionData schemaData = SchemaCompletionDataItems[namespaceUri]; + if (schemaData != null) { + if (File.Exists(schemaData.FileName)) { + File.Delete(schemaData.FileName); + } + SchemaCompletionDataItems.Remove(schemaData); + OnUserSchemaRemoved(); + } + } + + /// + /// Adds the schema to the user schemas folder and makes the + /// schema available to the xml editor. + /// + public static void AddUserSchema(XmlSchemaCompletionData schemaData) + { + if (SchemaCompletionDataItems[schemaData.NamespaceUri] == null) { + + if (!Directory.Exists(UserSchemaFolder)) { + Directory.CreateDirectory(UserSchemaFolder); + } + + string fileName = Path.GetFileName(schemaData.FileName); + string destinationFileName = Path.Combine(UserSchemaFolder, fileName); + File.Copy(schemaData.FileName, destinationFileName); + schemaData.FileName = destinationFileName; + SchemaCompletionDataItems.Add(schemaData); + OnUserSchemaAdded(); + } else { + LoggingService.Warn("Trying to add a schema that already exists. Namespace=" + schemaData.NamespaceUri); + } + } + + /// + /// Reads the system and user added schemas. + /// + void ReadSchemas() + { + // MSBuild schemas are in framework directory: + ReadSchemas(RuntimeEnvironment.GetRuntimeDirectory(), true); + ReadSchemas(SchemaFolder, true); + ReadSchemas(UserSchemaFolder, false); + } + + /// + /// Reads all .xsd files in the specified folder. + /// + void ReadSchemas(string folder, bool readOnly) + { + if (Directory.Exists(folder)) { + foreach (string fileName in Directory.GetFiles(folder, "*.xsd")) { + ReadSchema(fileName, readOnly); + } + } + } + + /// + /// Reads an individual schema and adds it to the collection. + /// + /// + /// If the schema namespace exists in the collection it is not added. + /// + void ReadSchema(string fileName, bool readOnly) + { + try { + string baseUri = XmlSchemaCompletionData.GetUri(fileName); + XmlSchemaCompletionData data = new XmlSchemaCompletionData(baseUri, fileName); + if (data.NamespaceUri != null) { + if (schemas[data.NamespaceUri] == null) { + data.ReadOnly = readOnly; + schemas.Add(data); + } else { + // Namespace already exists. + LoggingService.Warn("Ignoring duplicate schema namespace " + data.NamespaceUri); + } + } else { + // Namespace is null. + LoggingService.Warn("Ignoring schema with no namespace " + data.FileName); + } + } catch (Exception ex) { + LoggingService.Warn("Unable to read schema '" + fileName + "'. ", ex); + } + } + + /// + /// Gets the folder where the schemas for all users on the + /// local machine are stored. + /// + static string SchemaFolder { + get { + return Path.Combine(PropertyService.DataDirectory, "schemas"); + } + } + + /// + /// Gets the folder where schemas are stored for an individual user. + /// + static string UserSchemaFolder { + get { + return Path.Combine(PropertyService.ConfigDirectory, "schemas"); + } + } + + /// + /// Should really pass schema info with the event. + /// + static void OnUserSchemaAdded() + { + if (UserSchemaAdded != null) { + UserSchemaAdded(manager, new EventArgs()); + } + } + + /// + /// Should really pass schema info with the event. + /// + static void OnUserSchemaRemoved() + { + if (UserSchemaRemoved != null) { + UserSchemaRemoved(manager, new EventArgs()); + } + } + } + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemasPanel.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemasPanel.cs new file mode 100644 index 0000000000..bdc5faa5ce --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlSchemasPanel.cs @@ -0,0 +1,412 @@ +// +// +// +// +// $Revision: 3568 $ +// + +using ICSharpCode.XmlBinding.Gui; +using System; +using System.Collections.Specialized; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Gui.OptionPanels; + +namespace ICSharpCode.XmlEditor +{ +/* + /// + /// Shows the xml schemas that SharpDevelop knows about. + /// + public class XmlSchemasPanel : XmlFormsOptionPanel + { + ListBox schemaListBox; + Button removeButton; + ComboBox fileExtensionComboBox; + TextBox schemaTextBox; + TextBox namespacePrefixTextBox; + + bool ignoreNamespacePrefixTextChanges; + + bool changed; + XmlSchemaCompletionDataCollection addedSchemas = new XmlSchemaCompletionDataCollection(); + StringCollection removedSchemaNamespaces = new StringCollection(); + + SolidBrush readonlyTextBrush = new SolidBrush(SystemColors.GrayText); + SolidBrush selectedTextBrush = new SolidBrush(SystemColors.HighlightText); + SolidBrush normalTextBrush = new SolidBrush(SystemColors.WindowText); + + /// + /// Initialises the panel. + /// + public override void LoadPanelContents() + { + SetupFromXmlStream(this.GetType().Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.XmlSchemasPanel.xfrm")); + + schemaListBox = (ListBox)ControlDictionary["schemaListBox"]; + schemaListBox.DrawMode = DrawMode.OwnerDrawFixed; + schemaListBox.DrawItem += new DrawItemEventHandler(SchemaListBoxDrawItem); + schemaListBox.Sorted = true; + PopulateSchemaListBox(); + schemaListBox.SelectedIndexChanged += new EventHandler(SchemaListBoxSelectedIndexChanged); + + namespacePrefixTextBox = (TextBox)ControlDictionary["namespacePrefixTextBox"]; + namespacePrefixTextBox.TextChanged += new EventHandler(NamespacePrefixTextBoxTextChanged); + + ControlDictionary["addButton"].Click += new EventHandler(AddButtonClick); + removeButton = (Button)ControlDictionary["removeButton"]; + removeButton.Click += new EventHandler(RemoveButtonClick); + removeButton.Enabled = false; + + ControlDictionary["changeSchemaButton"].Click += new EventHandler(ChangeSchemaButtonClick); + + schemaTextBox = (TextBox)ControlDictionary["schemaTextBox"]; + fileExtensionComboBox = (ComboBox)ControlDictionary["fileExtensionComboBox"]; + fileExtensionComboBox.Sorted = true; + PopulateFileExtensionComboBox(); + fileExtensionComboBox.SelectedIndexChanged += new EventHandler(FileExtensionComboBoxSelectedIndexChanged); + } + + /// + /// Saves any changes to the configured schemas. + /// + public override bool StorePanelContents() + { + if (changed) { + try { + return SaveSchemaChanges(); + } catch (Exception ex) { + MessageService.ShowError(ex, "${res:ICSharpCode.XmlEditor.XmlSchemasPanel.UnableToSaveChanges}"); + return false; + } + } + + // Update schema associations after we have added any new schemas to + // the schema manager. + UpdateSchemaAssociations(); + + return true; + } + + void AddButtonClick(object source, EventArgs e) + { + try { + // Browse for schema. + + string schemaFileName = BrowseForSchema(); + + // Add schema if the namespace does not already exist. + + if (schemaFileName.Length > 0) { + changed = AddSchema(schemaFileName); + } + } catch (Exception ex) { + MessageService.ShowError("${res:ICSharpCode.XmlEditor.XmlSchemasPanel.UnableToAddSchema}\n\n" + ex.Message); + } + } + + void RemoveButtonClick(object source, EventArgs e) + { + // Remove selected schema. + XmlSchemaListBoxItem item = schemaListBox.SelectedItem as XmlSchemaListBoxItem; + if (item != null) { + RemoveSchema(item.NamespaceUri); + changed = true; + } + } + + /// + /// Enables the remove button if a list box item is selected. + /// + void SchemaListBoxSelectedIndexChanged(object source, EventArgs e) + { + XmlSchemaListBoxItem item = schemaListBox.SelectedItem as XmlSchemaListBoxItem; + if (item != null) { + if (item.ReadOnly) { + removeButton.Enabled = false; + } else { + removeButton.Enabled = true; + } + } else { + removeButton.Enabled = false; + } + } + + /// + /// Adds the schema namespaces to the list. + /// + void PopulateSchemaListBox() + { + foreach (XmlSchemaCompletionData schema in XmlSchemaManager.SchemaCompletionDataItems) { + XmlSchemaListBoxItem item = new XmlSchemaListBoxItem(schema.NamespaceUri, schema.ReadOnly); + schemaListBox.Items.Add(item); + } + } + + /// + /// Saves any changes to the configured schemas. + /// + /// + bool SaveSchemaChanges() + { + RemoveUserSchemas(); + AddUserSchemas(); + + return true; + } + + /// + /// Allows the user to browse the file system for a schema. + /// + /// The schema file name the user selected; otherwise an + /// empty string. + string BrowseForSchema() + { + string fileName = String.Empty; + + using (OpenFileDialog openFileDialog = new OpenFileDialog()) { + openFileDialog.CheckFileExists = true; + openFileDialog.Multiselect = false; + openFileDialog.Filter = StringParser.Parse("${res:SharpDevelop.FileFilter.XmlSchemaFiles}|*.xsd|${res:SharpDevelop.FileFilter.AllFiles}|*.*"); + + if (openFileDialog.ShowDialog() == DialogResult.OK) { + fileName = openFileDialog.FileName; + } + } + + return fileName; + } + + /// + /// Loads the specified schema and adds it to an internal collection. + /// + /// The schema file is not copied to the user's schema folder + /// until they click the OK button. + /// if the schema namespace + /// does not already exist; otherwise + /// + bool AddSchema(string fileName) + { + // Load the schema. + XmlSchemaCompletionData schema = new XmlSchemaCompletionData(fileName); + + // Make sure the schema has a target namespace. + if (schema.NamespaceUri == null) { + MessageService.ShowErrorFormatted("${res:ICSharpCode.XmlEditor.XmlSchemasPanel.NoTargetNamespace}", Path.GetFileName(schema.FileName)); + return false; + } + + // Check that the schema does not exist. + if (SchemaNamespaceExists(schema.NamespaceUri)) { + MessageService.ShowErrorFormatted("${res:ICSharpCode.XmlEditor.XmlSchemasPanel.NamespaceExists}", schema.NamespaceUri); + return false; + } + + // Store the schema so we can add it later. + int index = schemaListBox.Items.Add(new XmlSchemaListBoxItem(schema.NamespaceUri)); + schemaListBox.SelectedIndex = index; + addedSchemas.Add(schema); + if (removedSchemaNamespaces.Contains(schema.NamespaceUri)) { + removedSchemaNamespaces.Remove(schema.NamespaceUri); + } + + return true; + } + + /// + /// Checks that the schema namespace does not already exist. + /// + bool SchemaNamespaceExists(string namespaceURI) + { + bool exists = true; + if ((XmlSchemaManager.SchemaCompletionDataItems[namespaceURI] == null) && + (addedSchemas[namespaceURI] == null)){ + exists = false; + } else { + // Makes sure it has not been flagged for removal. + exists = !removedSchemaNamespaces.Contains(namespaceURI); + } + return exists; + } + + /// + /// Schedules the schema for removal. + /// + void RemoveSchema(string namespaceUri) + { + RemoveListBoxItem(namespaceUri); + + XmlSchemaCompletionData addedSchema = addedSchemas[namespaceUri]; + if (addedSchema != null) { + addedSchemas.Remove(addedSchema); + } else { + removedSchemaNamespaces.Add(namespaceUri); + } + } + + void RemoveListBoxItem(string namespaceUri) + { + foreach (XmlSchemaListBoxItem item in schemaListBox.Items) { + if (item.NamespaceUri == namespaceUri) { + schemaListBox.Items.Remove(item); + break; + } + } + } + + /// + /// Removes the schemas from the schema manager. + /// + void RemoveUserSchemas() + { + while (removedSchemaNamespaces.Count > 0) { + XmlSchemaManager.RemoveUserSchema(removedSchemaNamespaces[0]); + removedSchemaNamespaces.RemoveAt(0); + } + } + + /// + /// Adds the schemas to the schema manager. + /// + void AddUserSchemas() + { + while (addedSchemas.Count > 0) { + XmlSchemaManager.AddUserSchema(addedSchemas[0]); + addedSchemas.RemoveAt(0); + } + } + + /// + /// Draws the list box items so we can show the read only schemas in + /// a different colour. + /// + void SchemaListBoxDrawItem(object sender, DrawItemEventArgs e) + { + e.DrawBackground(); + + if (e.Index >= 0) { + XmlSchemaListBoxItem item = (XmlSchemaListBoxItem)schemaListBox.Items[e.Index]; + + if (IsListBoxItemSelected(e.State)) { + e.Graphics.DrawString(item.NamespaceUri, schemaListBox.Font, selectedTextBrush, e.Bounds); + } else if (item.ReadOnly) { + e.Graphics.DrawString(item.NamespaceUri, schemaListBox.Font, readonlyTextBrush, e.Bounds); + } else { + e.Graphics.DrawString(item.NamespaceUri, schemaListBox.Font, normalTextBrush, e.Bounds); + } + } + + e.DrawFocusRectangle(); + } + + bool IsListBoxItemSelected(DrawItemState state) + { + return ((state & DrawItemState.Selected) == DrawItemState.Selected); + } + + /// + /// Shows the namespace associated with the selected xml file extension. + /// + void FileExtensionComboBoxSelectedIndexChanged(object source, EventArgs e) + { + schemaTextBox.Text = String.Empty; + ignoreNamespacePrefixTextChanges = true; + namespacePrefixTextBox.Text = String.Empty; + + try { + XmlSchemaAssociationListBoxItem association = fileExtensionComboBox.SelectedItem as XmlSchemaAssociationListBoxItem; + if (association != null) { + schemaTextBox.Text = association.NamespaceUri; + namespacePrefixTextBox.Text = association.NamespacePrefix; + } + } finally { + ignoreNamespacePrefixTextChanges = false; + } + } + + /// + /// Allows the user to change the schema associated with an xml file + /// extension. + /// + void ChangeSchemaButtonClick(object source, EventArgs e) + { + string[] namespaces = GetSchemaListBoxNamespaces(); + using (SelectXmlSchemaForm form = new SelectXmlSchemaForm(namespaces)) { + form.SelectedNamespaceUri = schemaTextBox.Text; + if (form.ShowDialog(this) == DialogResult.OK) { + schemaTextBox.Text = form.SelectedNamespaceUri; + XmlSchemaAssociationListBoxItem item = (XmlSchemaAssociationListBoxItem)fileExtensionComboBox.SelectedItem; + item.NamespaceUri = form.SelectedNamespaceUri; + item.IsDirty = true; + } + } + } + + /// + /// Reads the configured xml file extensions and their associated namespaces. + /// + void PopulateFileExtensionComboBox() + { + string [] extensions = XmlDisplayBinding.GetXmlFileExtensions(); + + foreach (string extension in extensions) { + XmlSchemaAssociation association = XmlEditorAddInOptions.GetSchemaAssociation(extension); + XmlSchemaAssociationListBoxItem item = new XmlSchemaAssociationListBoxItem(association.Extension, association.NamespaceUri, association.NamespacePrefix); + fileExtensionComboBox.Items.Add(item); + } + + if (fileExtensionComboBox.Items.Count > 0) { + fileExtensionComboBox.SelectedIndex = 0; + FileExtensionComboBoxSelectedIndexChanged(this, new EventArgs()); + } + } + + /// + /// Updates the configured file extension to namespace mappings. + /// + void UpdateSchemaAssociations() + { + foreach (XmlSchemaAssociationListBoxItem item in fileExtensionComboBox.Items) { + if (item.IsDirty) { + XmlSchemaAssociation association = new XmlSchemaAssociation(item.Extension, item.NamespaceUri, item.NamespacePrefix); + XmlEditorAddInOptions.SetSchemaAssociation(association); + } + } + } + + /// + /// Returns an array of schema namespace strings that will be displayed + /// when the user chooses to associated a namespace to a file extension + /// by default. + /// + string[] GetSchemaListBoxNamespaces() + { + string[] namespaces = new string[schemaListBox.Items.Count]; + + for (int i = 0; i < schemaListBox.Items.Count; ++i) { + XmlSchemaListBoxItem item = (XmlSchemaListBoxItem)schemaListBox.Items[i]; + namespaces[i] = item.NamespaceUri; + } + + return namespaces; + } + + /// + /// User has changed the namespace prefix. + /// + void NamespacePrefixTextBoxTextChanged(object source, EventArgs e) + { + if (!ignoreNamespacePrefixTextChanges) { + XmlSchemaAssociationListBoxItem item = fileExtensionComboBox.SelectedItem as XmlSchemaAssociationListBoxItem; + if (item != null) { + item.NamespacePrefix = namespacePrefixTextBox.Text; + item.IsDirty = true; + } + } + } + } + * */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTextTreeNode.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTextTreeNode.cs new file mode 100644 index 0000000000..d30dbe4fcb --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTextTreeNode.cs @@ -0,0 +1,60 @@ +// +// +// +// +// $Revision: 2164 $ +// + +using System; +using System.Xml; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Represents an XmlText node in the tree. + /// + public class XmlTextTreeNode : XmlCharacterDataTreeNode + { + public const string XmlTextTreeNodeImageKey = "XmlTextTreeNodeImage"; + public const string XmlTextTreeNodeGhostImageKey = "XmlTextTreeNodeGhostImage"; + + XmlText xmlText; + + public XmlTextTreeNode(XmlText xmlText) + : base(xmlText) + { + this.xmlText = xmlText; + ImageKey = XmlTextTreeNodeImageKey; + SelectedImageKey = ImageKey; + Update(); + } + + /// + /// Gets the XmlText associated with this tree node. + /// + public XmlText XmlText { + get { + return xmlText; + } + } + + /// + /// Gets or sets whether to show the ghost image which is + /// displayed when cutting the node. + /// + public bool ShowGhostImage { + get { + return ImageKey == XmlTextTreeNodeGhostImageKey; + } + set { + if (value) { + ImageKey = XmlTextTreeNodeGhostImageKey; + } else { + ImageKey = XmlTextTreeNodeImageKey; + } + SelectedImageKey = ImageKey; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeEditor.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeEditor.cs new file mode 100644 index 0000000000..d9a71ef311 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeEditor.cs @@ -0,0 +1,654 @@ +// +// +// +// +// $Revision: 3908 $ +// + +using ICSharpCode.XmlBinding.Parser; +using System; +using System.Collections.Generic; +using System.Net; +using System.Xml; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlEditor +{ + /// + /// The class that is responsible for controlling the editing of the + /// Xml tree view. + /// + public class XmlTreeEditor + { + IXmlTreeView view; + XmlDocument document; + //XmlCompletionDataProvider completionDataProvider; + XmlNode copiedNode; + XmlNode cutNode; + + public XmlTreeEditor(IXmlTreeView view/*, XmlCompletionDataProvider completionDataProvider*/) + { + this.view = view; + //this.completionDataProvider = completionDataProvider; + } + + /// + /// Loads the xml into the editor. + /// + public void LoadXml(string xml) + { + try { + document = new XmlDocument(); + document.XmlResolver = null; + document.LoadXml(xml); + view.Document = document; + } catch (XmlException ex) { + view.ShowXmlIsNotWellFormedMessage(ex); + } catch (WebException ex) { + LoggingService.Debug(ex.ToString()); + view.ShowErrorMessage(ex.Message); + } + } + + /// + /// Gets the Xml document being edited. + /// + public XmlDocument Document { + get { + return document; + } + } + + /// + /// The selected tree node in the view has changed. + /// + public void SelectedNodeChanged() + { + XmlElement selectedElement = view.SelectedElement; + XmlText selectedTextNode = view.SelectedTextNode; + XmlComment selectedComment = view.SelectedComment; + if (selectedTextNode != null) { + view.ClearAttributes(); + view.ShowTextContent(selectedTextNode.InnerText); + } else if (selectedElement != null) { + view.TextContent = String.Empty; + view.ShowAttributes(selectedElement.Attributes); + } else if (selectedComment != null) { + view.ClearAttributes(); + view.ShowTextContent(selectedComment.InnerText); + } else { + view.ClearAttributes(); + view.TextContent = String.Empty; + } + } + + /// + /// The attribute value has changed. + /// + public void AttributeValueChanged() + { + view.IsDirty = true; + } + + /// + /// Adds one or more new attribute to the selected element. + /// + public void AddAttribute() + { + XmlElement selectedElement = view.SelectedElement; + if (selectedElement != null) { + string[] attributesNames = GetMissingAttributes(selectedElement); + string[] selectedAttributeNames = view.SelectNewAttributes(attributesNames); + if (selectedAttributeNames.Length > 0) { + foreach (string attributeName in selectedAttributeNames) { + selectedElement.SetAttribute(attributeName, String.Empty); + } + view.IsDirty = true; + view.ShowAttributes(selectedElement.Attributes); + } + } + } + + /// + /// Removes the selected attribute from the xml document. + /// + public void RemoveAttribute() + { + XmlElement selectedElement = view.SelectedElement; + if (selectedElement != null) { + string attribute = view.SelectedAttribute; + if (attribute != null) { + selectedElement.RemoveAttribute(attribute); + view.IsDirty = true; + view.ShowAttributes(selectedElement.Attributes); + } + } + } + + /// + /// The text content has been changed in the view. + /// + public void TextContentChanged() + { + XmlText textNode = view.SelectedTextNode; + XmlComment comment = view.SelectedComment; + if (textNode != null) { + view.IsDirty = true; + textNode.InnerText = view.TextContent; + view.UpdateTextNode(textNode); + } else if (comment != null) { + view.IsDirty = true; + comment.InnerText = view.TextContent; + view.UpdateComment(comment); + } + } + + /// + /// Adds a new child element to the selected element. + /// + public void AppendChildElement() + { + XmlElement selectedElement = view.SelectedElement; + if (selectedElement != null) { + string[] elementNames = GetChildElements(selectedElement); + string[] selectedElementNames = view.SelectNewElements(elementNames); + if (selectedElementNames.Length > 0) { + view.IsDirty = true; + foreach (string elementName in selectedElementNames) { + XmlElement newElement = document.CreateElement(elementName, selectedElement.NamespaceURI); + AppendChildElement(selectedElement, newElement); + } + } + } + } + + /// + /// Inserts an element before the currently selected element. + /// + public void InsertElementBefore() + { + XmlElement parentElement = null; + XmlNode selectedNode = view.SelectedElement; + if (selectedNode == null) { + selectedNode = view.SelectedComment; + } + if (selectedNode != null) { + parentElement = selectedNode.ParentNode as XmlElement; + } + + if (parentElement != null) { + string[] elementNames = GetChildElements(parentElement); + string[] selectedElementNames = view.SelectNewElements(elementNames); + if (selectedElementNames.Length > 0) { + view.IsDirty = true; + foreach (string elementName in selectedElementNames) { + XmlElement newElement = document.CreateElement(elementName, parentElement.NamespaceURI); + parentElement.InsertBefore(newElement, selectedNode); + view.InsertElementBefore(newElement); + } + } + } + } + + /// + /// Inserts an element after the currently selected element. + /// + public void InsertElementAfter() + { + XmlElement parentElement = null; + XmlNode selectedNode = view.SelectedElement; + if (selectedNode == null) { + selectedNode = view.SelectedComment; + } + if (selectedNode != null) { + parentElement = selectedNode.ParentNode as XmlElement; + } + + if (parentElement != null) { + string[] elementNames = GetChildElements(parentElement); + string[] selectedElementNames = view.SelectNewElements(elementNames); + if (selectedElementNames.Length > 0) { + view.IsDirty = true; + foreach (string elementName in selectedElementNames) { + XmlElement newElement = document.CreateElement(elementName, parentElement.NamespaceURI); + parentElement.InsertAfter(newElement, selectedNode); + view.InsertElementAfter(newElement); + } + } + } + } + + /// + /// Adds a child text node to the current selected element. + /// + public void AppendChildTextNode() + { + AppendChildTextNode(document.CreateTextNode(String.Empty)); + } + + /// + /// Inserts a text node before the currently selected node. + /// + public void InsertTextNodeBefore() + { + // Get the currently selected text node or element. + XmlNode selectedNode = GetSelectedCommentOrElementOrTextNode(); + + // Insert the text node before the selected node. + if (selectedNode != null) { + XmlElement parentElement = selectedNode.ParentNode as XmlElement; + if (parentElement != null) { + XmlText textNode = document.CreateTextNode(String.Empty); + parentElement.InsertBefore(textNode, selectedNode); + view.IsDirty = true; + view.InsertTextNodeBefore(textNode); + } + } + } + + /// + /// Inserts a text node after the currently selected node. + /// + public void InsertTextNodeAfter() + { + // Get the currently selected text node or element. + XmlNode selectedNode = GetSelectedCommentOrElementOrTextNode(); + + // Insert the text node after the selected node. + if (selectedNode != null) { + XmlElement parentElement = selectedNode.ParentNode as XmlElement; + if (parentElement != null) { + XmlText textNode = document.CreateTextNode(String.Empty); + parentElement.InsertAfter(textNode, selectedNode); + view.IsDirty = true; + view.InsertTextNodeAfter(textNode); + } + } + } + + /// + /// Adds a child comment node to the current selected element. + /// + public void AppendChildComment() + { + XmlComment comment = document.CreateComment(String.Empty); + AppendChildComment(comment); + } + + /// + /// Inserts a comment before the selected node. + /// + public void InsertCommentBefore() + { + XmlNode node = GetSelectedCommentOrElementOrTextNode(); + if (node != null) { + XmlNode parentNode = node.ParentNode; + XmlComment comment = document.CreateComment(String.Empty); + parentNode.InsertBefore(comment, node); + view.IsDirty = true; + view.InsertCommentBefore(comment); + } + } + + /// + /// Inserts a comment after the selected node. + /// + public void InsertCommentAfter() + { + XmlNode node = GetSelectedCommentOrElementOrTextNode(); + if (node != null) { + XmlNode parentNode = node.ParentNode; + XmlComment comment = document.CreateComment(String.Empty); + parentNode.InsertAfter(comment, node); + view.IsDirty = true; + view.InsertCommentAfter(comment); + } + } + + /// + /// Deletes the selected tree node from the xml document. + /// + public void Delete() + { + XmlNode selectedNode = view.SelectedNode; + XmlElement selectedElement = selectedNode as XmlElement; + XmlComment selectedComment = selectedNode as XmlComment; + XmlText selectedText = selectedNode as XmlText; + if (selectedElement != null) { + RemoveElement(selectedElement); + } else if (selectedComment != null) { + RemoveComment(selectedComment); + } else if (selectedText != null) { + RemoveTextNode(selectedText); + } + } + + /// + /// Copies the selected node. + /// + public void Copy() + { + copiedNode = view.SelectedNode; + if (cutNode != null) { + view.HideCut(cutNode); + } + } + + /// + /// Pastes the copied or cut node as a child of the selected node. + /// + public void Paste() + { + if (IsPasteEnabled) { + if (copiedNode != null) { + AppendChildCopy(copiedNode); + } else { + CutAndPasteNode(cutNode); + } + } + } + + /// + /// Cuts the selected node. + /// + public void Cut() + { + cutNode = view.SelectedNode; + if (cutNode != null) { + view.ShowCut(cutNode); + } + copiedNode = null; + } + + /// + /// Gets whether the cut method is enabled. + /// + public bool IsCutEnabled { + get { + XmlNode selectedNode = view.SelectedNode; + return selectedNode != null && document.DocumentElement != selectedNode; + } + } + + /// + /// Gets whether the copy method is enabled. + /// + public bool IsCopyEnabled { + get { + return view.SelectedNode != null; + } + } + + /// + /// Gets whether the paste method is enabled. + /// + public bool IsPasteEnabled { + get { + XmlNode destinationNode = view.SelectedNode; + if (destinationNode != null) { + XmlNode sourceNode = copiedNode ?? cutNode; + if (sourceNode != null) { + return GetPasteEnabled(sourceNode, destinationNode); + } + } + return false; + } + } + + /// + /// Gets whether the delete method is enabled. + /// + public bool IsDeleteEnabled { + get { + return view.SelectedNode != null; + } + } + + /// + /// Gets the missing attributes for the specified element based + /// on its associated schema. + /// + string[] GetMissingAttributes(XmlElement element) + { + XmlElementPath elementPath = GetElementPath(element); + // TODO : implement + List attributes = new List(); +// XmlSchemaCompletionData schemaCompletionData = completionDataProvider.FindSchema(elementPath); +// if (schemaCompletionData != null) { +// ICompletionData[] completionData = schemaCompletionData.GetAttributeCompletionData(elementPath); +// foreach (ICompletionData attributeCompletionData in completionData) { +// // Ignore existing attributes. +// string attributeName = attributeCompletionData.Text; +// if (!element.HasAttribute(attributeName)) { +// attributes.Add(attributeName); +// } +// } +// } + return attributes.ToArray(); + } + + /// + /// Returns the path to the specified element starting from the + /// root element. + /// + XmlElementPath GetElementPath(XmlElement element) + { + XmlElementPath path = new XmlElementPath(); + XmlElement parentElement = element; + while (parentElement != null) { + QualifiedName name = new QualifiedName(parentElement.LocalName, parentElement.NamespaceURI, parentElement.Prefix); + path.Elements.Insert(0, name); + + // Move to parent element. + parentElement = parentElement.ParentNode as XmlElement; + } + return path; + } + + /// + /// Returns a list of elements that can be children of the + /// specified element. + /// + string[] GetChildElements(XmlElement element) + { + XmlElementPath elementPath = GetElementPath(element); + + List elements = new List(); +// XmlSchemaCompletionData schemaCompletionData = completionDataProvider.FindSchema(elementPath); +// if (schemaCompletionData != null) { +// ICompletionData[] completionData = schemaCompletionData.GetChildElementCompletionData(elementPath); +// foreach (ICompletionData elementCompletionData in completionData) { +// elements.Add(elementCompletionData.Text); +// } +// } + return elements.ToArray(); + } + + /// + /// Gets the current comment or element or text node + /// from the view. + /// + XmlNode GetSelectedCommentOrElementOrTextNode() + { + XmlNode node = view.SelectedComment; + if (node != null) { + return node; + } + return GetSelectedElementOrTextNode(); + } + + /// + /// Gets the currently selected element or text node. + /// + XmlNode GetSelectedElementOrTextNode() + { + XmlNode node = view.SelectedTextNode; + if (node != null) { + return node; + } + return view.SelectedElement; + } + + /// + /// Appends the specified element as a child to the selected element. + /// + void AppendChildElement(XmlElement element) + { + AppendChildElement(view.SelectedElement, element); + } + + /// + /// Appends the specified element as a child to the selected element. + /// + void AppendChildElement(XmlElement selectedElement, XmlElement element) + { + selectedElement.AppendChild(element); + view.AppendChildElement(element); + view.IsDirty = true; + } + + /// + /// Removes the specified element from the document. + /// + void RemoveElement(XmlElement element) + { + XmlNode parentNode = element.ParentNode; + parentNode.RemoveChild(element); + view.IsDirty = true; + view.RemoveElement(element); + } + + /// + /// Removes the specified comment from the document. + /// + void RemoveComment(XmlComment comment) + { + XmlNode parentNode = comment.ParentNode; + parentNode.RemoveChild(comment); + view.IsDirty = true; + view.RemoveComment(comment); + } + + /// + /// Removes the specified text node from the document. + /// + void RemoveTextNode(XmlText textNode) + { + XmlNode parentNode = textNode.ParentNode; + parentNode.RemoveChild(textNode); + view.IsDirty = true; + view.RemoveTextNode(textNode); + } + + /// + /// Gets whether the source node can be pasted as a child + /// node of the destination node. + /// + static bool GetPasteEnabled(XmlNode source, XmlNode destination) + { + if (source is XmlElement || source is XmlText || source is XmlComment) { + return destination is XmlElement; + } + return false; + } + + /// + /// Takes a copy of the node and appends it to the selected + /// node. + /// + void AppendChildCopy(XmlNode nodeToCopy) + { + if (nodeToCopy is XmlElement) { + XmlElement copy = (XmlElement)nodeToCopy.CloneNode(true); + AppendChildElement(copy); + } else if (nodeToCopy is XmlText) { + XmlText copy = (XmlText)nodeToCopy.CloneNode(true); + AppendChildTextNode(copy); + } else if (nodeToCopy is XmlComment) { + XmlComment copy = (XmlComment)nodeToCopy.CloneNode(true); + AppendChildComment(copy); + } + } + + /// + /// Appends the specified text node to the currently selected element. + /// + void AppendChildTextNode(XmlText textNode) + { + XmlElement selectedElement = view.SelectedElement; + if (selectedElement != null) { + selectedElement.AppendChild(textNode); + view.IsDirty = true; + view.AppendChildTextNode(textNode); + } + } + + /// + /// Cuts from the tree and pastes it to the currently selected node. + /// + void CutAndPasteNode(XmlNode node) + { + XmlElement cutElement = node as XmlElement; + XmlText cutTextNode = node as XmlText; + XmlComment cutCommentNode = node as XmlComment; + if (cutElement != null) { + CutAndPasteElement(cutElement); + } else if (cutTextNode != null) { + CutAndPasteTextNode(cutTextNode); + } else if (cutCommentNode != null) { + CutAndPasteComment(cutCommentNode); + } + cutNode = null; + } + + /// + /// Cuts the element from the document and pastes it as a child + /// of the selected element. + /// + void CutAndPasteElement(XmlElement element) + { + if (element != view.SelectedElement) { + view.RemoveElement(element); + AppendChildElement(element); + } else { + // Pasting to the same cut element so just + // change the tree icon back to the uncut state. + view.HideCut(element); + } + } + + /// + /// Cuts the text node from the document and appends it as a child + /// of the currently selected element. + /// + void CutAndPasteTextNode(XmlText text) + { + view.RemoveTextNode(text); + AppendChildTextNode(text); + } + + /// + /// Appends the specified comment to the currently selected element. + /// + void AppendChildComment(XmlComment comment) + { + XmlElement selectedElement = view.SelectedElement; + if (selectedElement != null) { + selectedElement.AppendChild(comment); + view.IsDirty = true; + view.AppendChildComment(comment); + } + } + + /// + /// Cuts the comment node from the document and appends it as a child + /// of the currently selected element. + /// + void CutAndPasteComment(XmlComment comment) + { + view.RemoveComment(comment); + AppendChildComment(comment); + } + } +} + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewContainerControl.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewContainerControl.cs new file mode 100644 index 0000000000..2363e83282 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewContainerControl.cs @@ -0,0 +1,924 @@ +// +// +// +// +// $Revision: 3623 $ +// + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Xml; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// This user control holds both the XmlTreeViewControl and the + /// attributes property grid in a split container. This is separate from + /// the XmlTreeView class so we can use the forms designer to design this control. + /// + public class XmlTreeViewContainerControl : System.Windows.Forms.UserControl, IXmlTreeView, IOwnerState, IClipboardHandler + { + XmlTreeEditor editor; + bool dirty; + bool errorMessageTextBoxVisible; + bool attributesGridVisible = true; + bool textBoxVisible; + + [Flags] + public enum XmlTreeViewContainerControlState { + Nothing = 0, + ElementSelected = 1, + RootElementSelected = 2, + AttributeSelected = 4, + TextNodeSelected = 8, + CommentSelected = 16 + } + + public event EventHandler DirtyChanged; + + public XmlTreeViewContainerControl() + { + InitializeComponent(); + InitImages(); + } + + /// + /// Gets the current XmlTreeViewContainerControlState. + /// + public Enum InternalState { + get { + XmlTreeViewContainerControlState state = XmlTreeViewContainerControlState.Nothing; + if (SelectedElement != null) { + state |= XmlTreeViewContainerControlState.ElementSelected; + if (SelectedElement == Document.DocumentElement) { + state |= XmlTreeViewContainerControlState.RootElementSelected; + } + } + if (SelectedAttribute != null) { + state |= XmlTreeViewContainerControlState.AttributeSelected; + } + if (SelectedTextNode != null) { + state = XmlTreeViewContainerControlState.TextNodeSelected; + } + if (SelectedComment != null) { + state = XmlTreeViewContainerControlState.CommentSelected; + } + return state; + } + } + + /// + /// Gets the property grid that displays attributes for the + /// selected xml element. + /// + public PropertyGrid AttributesGrid { + get { + return attributesGrid; + } + } + + /// + /// Gets or sets whether the xml document needs saving. + /// + public bool IsDirty { + get { + return dirty; + } + set { + bool previousDirty = dirty; + dirty = value; + OnXmlChanged(previousDirty); + } + } + + /// + /// Gets or sets the error message to display. + /// + public string ErrorMessage { + get { + return errorMessageTextBox.Text; + } + set { + errorMessageTextBox.Text = value; + } + } + + /// + /// Gets or sets whether the error message is visible. When visible the + /// error message text box replaces the property grid. + /// + public bool IsErrorMessageTextBoxVisible { + get { + return errorMessageTextBoxVisible; + } + set { + errorMessageTextBoxVisible = value; + if (value) { + errorMessageTextBox.BringToFront(); + errorMessageTextBox.TabStop = true;; + IsAttributesGridVisible = false; + IsTextBoxVisible = false; + } else { + errorMessageTextBox.SendToBack(); + errorMessageTextBox.TabStop = false; + } + } + } + + /// + /// Gets the XmlTreeView in the container. + /// + public XmlTreeViewControl TreeView { + get { + return xmlElementTreeView; + } + } + + public void ShowXmlIsNotWellFormedMessage(XmlException ex) + { + ShowErrorMessage(ex.Message); + } + + public void ShowErrorMessage(string message) + { + xmlElementTreeView.Clear(); + ErrorMessage = message; + IsErrorMessageTextBoxVisible = true; + } + + /// + /// Displays the specified xml as a tree. + /// + public void LoadXml(string xml/*, XmlCompletionDataProvider completionDataProvider*/) + { + textBox.Clear(); + IsAttributesGridVisible = true; + ClearAttributes(); + + editor = new XmlTreeEditor(this); + editor.LoadXml(xml); + + // Expand document element node. + if (xmlElementTreeView.Nodes.Count > 0) { + xmlElementTreeView.Nodes[0].Expand(); + } + } + + /// + /// Gets or sets the xml document to be shown in this + /// container control. + /// + public XmlDocument Document { + get { + return editor.Document; + } + set { + xmlElementTreeView.Document = value; + } + } + + /// + /// Shows the attributes. + /// + public void ShowAttributes(XmlAttributeCollection attributes) + { + IsAttributesGridVisible = true; + attributesGrid.SelectedObject = new XmlAttributeTypeDescriptor(attributes); + } + + /// + /// Clears all the attributes currently on display. + /// + public void ClearAttributes() + { + attributesGrid.SelectedObject = null; + } + + /// + /// Shows the xml element's text content after the user has + /// selected the text node. + /// + public void ShowTextContent(string text) + { + IsTextBoxVisible = true; + textBox.Text = text; + } + + /// + /// Gets or sets the text of the text node or + /// comment node currently on display. + /// + public string TextContent { + get { + return textBox.Text; + } + set { + textBox.Text = value; + } + } + + /// + /// Gets the currently selected node based on what is selected in + /// the tree. This does not return the selected attribute. + /// + public XmlNode SelectedNode { + get { + XmlElement selectedElement = SelectedElement; + if (selectedElement != null) { + return selectedElement; + } + + XmlText selectedTextNode = SelectedTextNode; + if (selectedTextNode != null) { + return selectedTextNode; + } + + return SelectedComment; + } + } + + /// + /// Gets the element currently selected. + /// + public XmlElement SelectedElement { + get { + return xmlElementTreeView.SelectedElement; + } + } + + /// + /// Gets the text node currently selected. + /// + public XmlText SelectedTextNode { + get { + return xmlElementTreeView.SelectedTextNode; + } + } + + /// + /// Gets the comment node currently selected. + /// + public XmlComment SelectedComment { + get { + return xmlElementTreeView.SelectedComment; + } + } + + /// + /// Gets the name of the attribute currently selected. + /// + public string SelectedAttribute { + get { + GridItem gridItem = attributesGrid.SelectedGridItem; + if (IsAttributesGridVisible && gridItem != null && gridItem.PropertyDescriptor != null) { + return gridItem.PropertyDescriptor.Name; + } + return null; + } + } + + /// + /// Shows the add attribute dialog so the user can add a new + /// attribute to the XML tree. + /// + public void AddAttribute() + { + editor.AddAttribute(); + } + + /// + /// Shows the add attribute dialog so the user can choose one or more + /// new attributes to be added to the selected element. + /// + /// The list of attributes the user + /// can choose from. + /// The attributes selected by the user. + public string[] SelectNewAttributes(string[] attributes) + { + using (IAddXmlNodeDialog addAttributeDialog = CreateAddAttributeDialog(attributes)) { + if (addAttributeDialog.ShowDialog() == DialogResult.OK) { + return addAttributeDialog.GetNames(); + } + return new string[0]; + } + } + + /// + /// Removes the currently selected attribute. + /// + public void RemoveAttribute() + { + editor.RemoveAttribute(); + } + + /// + /// Shows the add element dialog so the user can choose one or more + /// new elements to be added to the selected element. + /// + /// The list of elements the user + /// can choose from. + /// The elements selected by the user. + public string[] SelectNewElements(string[] elements) + { + using (IAddXmlNodeDialog addElementDialog = CreateAddElementDialog(elements)) { + if (addElementDialog.ShowDialog() == DialogResult.OK) { + return addElementDialog.GetNames(); + } + return new string[0]; + } + } + + /// + /// Appends a new child element to the currently selected element. + /// + public void AppendChildElement(XmlElement element) + { + xmlElementTreeView.AppendChildElement(element); + } + + /// + /// Adds a new child element to the selected element. + /// + public void AddChildElement() + { + editor.AppendChildElement(); + } + + /// + /// Inserts an element before the currently selected element. + /// + public void InsertElementBefore() + { + editor.InsertElementBefore(); + } + + /// + /// Inserts the specified element before the currently selected + /// element. + /// + public void InsertElementBefore(XmlElement element) + { + xmlElementTreeView.InsertElementBefore(element); + } + + /// + /// Inserts an element after the currently selected element. + /// + public void InsertElementAfter() + { + editor.InsertElementAfter(); + } + + /// + /// Inserts the specified element after the currently selected + /// element. + /// + public void InsertElementAfter(XmlElement element) + { + xmlElementTreeView.InsertElementAfter(element); + } + + /// + /// Removes the specified element from the tree. + /// + public void RemoveElement(XmlElement element) + { + xmlElementTreeView.RemoveElement(element); + } + + /// + /// Appends a new text node to the currently selected + /// element. + /// + public void AppendChildTextNode(XmlText textNode) + { + xmlElementTreeView.AppendChildTextNode(textNode); + } + + /// + /// Appends a new text node to the currently selected + /// element. + /// + public void AppendChildTextNode() + { + editor.AppendChildTextNode(); + } + + /// + /// Inserts a new text node before the currently selected + /// node. + /// + public void InsertTextNodeBefore() + { + editor.InsertTextNodeBefore(); + } + + /// + /// Inserts a new text node before the currently selected + /// node. + /// + public void InsertTextNodeBefore(XmlText textNode) + { + xmlElementTreeView.InsertTextNodeBefore(textNode); + } + + /// + /// Inserts a new text node after the currently selected + /// node. + /// + public void InsertTextNodeAfter() + { + editor.InsertTextNodeAfter(); + } + + /// + /// Inserts a new text node after the currently selected + /// node. + /// + public void InsertTextNodeAfter(XmlText textNode) + { + xmlElementTreeView.InsertTextNodeAfter(textNode); + } + + /// + /// Removes the currently selected text node. + /// + public void RemoveTextNode(XmlText textNode) + { + xmlElementTreeView.RemoveTextNode(textNode); + } + + /// + /// Updates the corresponding tree node's text. + /// + public void UpdateTextNode(XmlText textNode) + { + xmlElementTreeView.UpdateTextNode(textNode); + } + + /// + /// Updates the corresponding tree node's text. + /// + public void UpdateComment(XmlComment comment) + { + xmlElementTreeView.UpdateComment(comment); + } + + /// + /// Appends a new child comment node to the currently selected + /// element. + /// + public void AppendChildComment(XmlComment comment) + { + xmlElementTreeView.AppendChildComment(comment); + } + + /// + /// Appends a new child comment node to the currently selected + /// element. + /// + public void AppendChildComment() + { + editor.AppendChildComment(); + } + + /// + /// Removes the specified xml comment from the tree. + /// + public void RemoveComment(XmlComment comment) + { + xmlElementTreeView.RemoveComment(comment); + } + + /// + /// Inserts the comment before the currently selected node. + /// + public void InsertCommentBefore(XmlComment comment) + { + xmlElementTreeView.InsertCommentBefore(comment); + } + + /// + /// Inserts a comment before the currently selected node. + /// + public void InsertCommentBefore() + { + editor.InsertCommentBefore(); + } + + /// + /// Inserts the comment after the currently selected node. + /// + public void InsertCommentAfter(XmlComment comment) + { + xmlElementTreeView.InsertCommentAfter(comment); + } + + /// + /// Inserts a comment after the currently selected node. + /// + public void InsertCommentAfter() + { + editor.InsertCommentAfter(); + } + + /// + /// Updates the view to show that the specified node is going + /// to be cut. + /// + public void ShowCut(XmlNode node) + { + xmlElementTreeView.ShowCut(node); + } + + /// + /// Updates the view so that the specified node is not displayed + /// as being cut. + /// + public void HideCut(XmlNode node) + { + xmlElementTreeView.HideCut(node); + } + + #region IClipboardHandler implementation + + /// + /// Gets whether cutting is enabled. + /// + public bool EnableCut { + get { + return editor.IsCutEnabled; + } + } + + /// + /// Gets whether copying is enabled. + /// + public bool EnableCopy { + get { + return editor.IsCopyEnabled; + } + } + + /// + /// Gets whether pasting is enabled. + /// + public bool EnablePaste { + get { + return editor.IsPasteEnabled; + } + } + + /// + /// Gets whether deleting is enabled. + /// + public bool EnableDelete { + get { + return editor.IsDeleteEnabled; + } + } + + /// + /// Currently not possible to select all tree nodes so this + /// always returns false. + /// + public bool EnableSelectAll { + get { + return false; + } + } + + /// + /// Cuts the selected tree node. + /// + public void Cut() + { + editor.Cut(); + } + + /// + /// Copies the selected tree node. + /// + public void Copy() + { + editor.Copy(); + } + + /// + /// Pastes the selected tree node. + /// + public void Paste() + { + editor.Paste(); + } + + /// + /// Deletes the selected tree node. + /// + public void Delete() + { + editor.Delete(); + } + + /// + /// Selects all tree nodes. Currently not supported. + /// + public void SelectAll() + { + } + + #endregion + + /// + /// Disposes resources used by the control. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// Creates a new AddElementDialog. + /// + /// The element names to be listed in the + /// dialog. + protected virtual IAddXmlNodeDialog CreateAddElementDialog(string[] elementNames) + { + AddXmlNodeDialog dialog = new AddXmlNodeDialog(elementNames); + dialog.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.AddElementDialog.Title}"); + dialog.CustomNameLabelText = StringParser.Parse("${res:ICSharpCode.XmlEditor.AddElementDialog.CustomElementLabel}"); + return dialog; + } + + /// + /// Creates a new AddAttributeDialog. + /// + /// The attribute names to be listed in the + /// dialog. + protected virtual IAddXmlNodeDialog CreateAddAttributeDialog(string[] attributeNames) + { + AddXmlNodeDialog dialog = new AddXmlNodeDialog(attributeNames); + dialog.Text = StringParser.Parse("${res:ICSharpCode.XmlEditor.AddAttributeDialog.Title}"); + dialog.CustomNameLabelText = StringParser.Parse("${res:ICSharpCode.XmlEditor.AddAttributeDialog.CustomAttributeLabel}"); + return dialog; + } + + /// + /// Deletes the selected node. + /// + protected void XmlElementTreeViewDeleteKeyPressed(object source, EventArgs e) + { + Delete(); + } + + #region Forms Designer generated code + + /// + /// Designer variable used to keep track of non-visual components. + /// + System.ComponentModel.IContainer components = null; + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + ICSharpCode.SharpDevelop.Gui.ExtTreeViewComparer extTreeViewComparer1 = new ICSharpCode.SharpDevelop.Gui.ExtTreeViewComparer(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.xmlElementTreeView = new ICSharpCode.XmlEditor.XmlTreeViewControl(); + this.attributesGrid = new System.Windows.Forms.PropertyGrid(); + this.errorMessageTextBox = new System.Windows.Forms.RichTextBox(); + this.textBox = new System.Windows.Forms.RichTextBox(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.xmlElementTreeView); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.attributesGrid); + this.splitContainer.Panel2.Controls.Add(this.errorMessageTextBox); + this.splitContainer.Panel2.Controls.Add(this.textBox); + this.splitContainer.Size = new System.Drawing.Size(562, 326); + this.splitContainer.SplitterDistance = 185; + this.splitContainer.SplitterWidth = 2; + this.splitContainer.TabIndex = 0; + this.splitContainer.TabStop = false; + // + // xmlElementTreeView + // + this.xmlElementTreeView.AllowDrop = true; + this.xmlElementTreeView.CanClearSelection = true; + this.xmlElementTreeView.Dock = System.Windows.Forms.DockStyle.Fill; + this.xmlElementTreeView.Document = null; + this.xmlElementTreeView.DrawMode = System.Windows.Forms.TreeViewDrawMode.OwnerDrawText; + this.xmlElementTreeView.HideSelection = false; + this.xmlElementTreeView.ImageIndex = 0; + this.xmlElementTreeView.IsSorted = false; + this.xmlElementTreeView.Location = new System.Drawing.Point(0, 0); + this.xmlElementTreeView.Name = "xmlElementTreeView"; + this.xmlElementTreeView.NodeSorter = extTreeViewComparer1; + this.xmlElementTreeView.SelectedImageIndex = 0; + this.xmlElementTreeView.Size = new System.Drawing.Size(185, 326); + this.xmlElementTreeView.TabIndex = 0; + this.xmlElementTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.XmlElementTreeViewAfterSelect); + this.xmlElementTreeView.DeleteKeyPressed += new System.EventHandler(this.XmlElementTreeViewDeleteKeyPressed); + // + // attributesGrid + // + this.attributesGrid.Dock = System.Windows.Forms.DockStyle.Fill; + this.attributesGrid.HelpVisible = false; + this.attributesGrid.Location = new System.Drawing.Point(0, 0); + this.attributesGrid.Name = "attributesGrid"; + this.attributesGrid.PropertySort = System.Windows.Forms.PropertySort.Alphabetical; + this.attributesGrid.Size = new System.Drawing.Size(375, 326); + this.attributesGrid.TabIndex = 1; + this.attributesGrid.ToolbarVisible = false; + this.attributesGrid.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.AttributesGridPropertyValueChanged); + // + // errorMessageTextBox + // + this.errorMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.errorMessageTextBox.Location = new System.Drawing.Point(0, 0); + this.errorMessageTextBox.Name = "errorMessageTextBox"; + this.errorMessageTextBox.Size = new System.Drawing.Size(375, 326); + this.errorMessageTextBox.TabIndex = 0; + this.errorMessageTextBox.TabStop = false; + this.errorMessageTextBox.Text = ""; + // + // textBox + // + this.textBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.textBox.Location = new System.Drawing.Point(0, 0); + this.textBox.Name = "textBox"; + this.textBox.Size = new System.Drawing.Size(375, 326); + this.textBox.TabIndex = 2; + this.textBox.TabStop = false; + this.textBox.Text = ""; + this.textBox.TextChanged += new System.EventHandler(this.TextBoxTextChanged); + // + // XmlTreeViewContainerControl + // + this.Controls.Add(this.splitContainer); + this.Name = "XmlTreeViewContainerControl"; + this.Size = new System.Drawing.Size(562, 326); + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + this.splitContainer.ResumeLayout(false); + this.ResumeLayout(false); + } + private System.Windows.Forms.RichTextBox textBox; + private System.Windows.Forms.PropertyGrid attributesGrid; + private System.Windows.Forms.RichTextBox errorMessageTextBox; + private ICSharpCode.XmlEditor.XmlTreeViewControl xmlElementTreeView; + private System.Windows.Forms.SplitContainer splitContainer; + + #endregion + + /// + /// This method is protected only so we can easily test + /// what happens when this method is called. Triggering + /// a TextChanged event is difficult to do from unit tests. + /// You can trigger it it by setting the textBox's Rtf property. + /// + protected void TextBoxTextChanged(object sender, EventArgs e) + { + if (editor != null) { + bool previousIsDirty = dirty; + editor.TextContentChanged(); + OnXmlChanged(previousIsDirty); + } + } + + /// + /// This method is protected so we can test it. + /// + protected void XmlElementTreeViewAfterSelect(object sender, TreeViewEventArgs e) + { + editor.SelectedNodeChanged(); + } + + /// + /// This method is protected so we can test it. + /// + protected void AttributesGridPropertyValueChanged(object s, PropertyValueChangedEventArgs e) + { + bool previousIsDirty = dirty; + editor.AttributeValueChanged(); + OnXmlChanged(previousIsDirty); + } + + /// + /// Creates an image list that will be used for the XmlTreeViewControl. + /// + void InitImages() + { + if (components == null) { + components = new Container(); + } + ImageList images = new ImageList(components); + + // Add xml element tree node images. + Image xmlElementImage = Image.FromStream(typeof(XmlTreeViewContainerControl).Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.XmlElementTreeNodeIcon.png")); + images.Images.Add(XmlElementTreeNode.XmlElementTreeNodeImageKey, xmlElementImage); + images.Images.Add(XmlElementTreeNode.XmlElementTreeNodeGhostImageKey, IconService.GetGhostBitmap(new Bitmap(xmlElementImage))); + + // Add text tree node images. + Image xmlTextImage = Image.FromStream(typeof(XmlTreeViewContainerControl).Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.XmlTextTreeNodeIcon.png")); + images.Images.Add(XmlTextTreeNode.XmlTextTreeNodeImageKey, xmlTextImage); + images.Images.Add(XmlTextTreeNode.XmlTextTreeNodeGhostImageKey, IconService.GetGhostBitmap(new Bitmap(xmlTextImage))); + + // Add comment tree node images. + Image xmlCommentImage = Image.FromStream(typeof(XmlTreeViewContainerControl).Assembly.GetManifestResourceStream("ICSharpCode.XmlEditor.Resources.XmlCommentTreeNodeIcon.png")); + images.Images.Add(XmlCommentTreeNode.XmlCommentTreeNodeImageKey, xmlCommentImage); + images.Images.Add(XmlCommentTreeNode.XmlCommentTreeNodeGhostImageKey, IconService.GetGhostBitmap(new Bitmap(xmlCommentImage))); + + xmlElementTreeView.ImageList = images; + } + + /// + /// Raises the dirty changed event if the dirty flag has changed. + /// + void OnXmlChanged(bool previousIsDirty) + { + if (previousIsDirty != dirty) { + OnDirtyChanged(); + } + } + + /// + /// Raises the DirtyChanged event. + /// + void OnDirtyChanged() + { + if (DirtyChanged != null) { + DirtyChanged(this, new EventArgs()); + } + } + + /// + /// Gets or sets whether the attributes grid is visible. + /// + bool IsAttributesGridVisible { + get { + return attributesGridVisible; + } + set { + attributesGridVisible = value; + if (value) { + attributesGrid.BringToFront(); + attributesGrid.TabStop = true; + IsTextBoxVisible = false; + IsErrorMessageTextBoxVisible = false; + } else { + attributesGrid.SendToBack(); + attributesGrid.TabStop = false; + } + } + } + + /// + /// Gets or sets whether the text node text box is visible. + /// + bool IsTextBoxVisible { + set { + textBoxVisible = value; + if (value) { + textBox.BringToFront(); + textBox.TabStop = true; + IsAttributesGridVisible = false; + IsErrorMessageTextBoxVisible = false; + } else { + textBox.SendToBack(); + textBox.TabStop = false; + } + } + } + } +} + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewControl.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewControl.cs new file mode 100644 index 0000000000..641877f97d --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Src/XmlTreeViewControl.cs @@ -0,0 +1,578 @@ +// +// +// +// +// $Revision: 2741 $ +// + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Xml; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Displays a tree of XML elements. This is a separate control so it can + /// be unit tested. It has no SharpDevelop specific parts, for example, + /// the context menus are defined in the XmlTreeViewContainerControl. + /// + public class XmlTreeViewControl : ExtTreeView + { + const string ViewStatePropertyName = "XmlTreeViewControl.ViewState"; + + XmlDocument document; + + enum InsertionMode { + Before = 0, + After = 1 + } + + /// + /// Raised when the delete key is pressed. + /// + public event EventHandler DeleteKeyPressed; + + public XmlTreeViewControl() + { + } + + /// + /// Gets or sets the xml document currently being displayed. + /// + [Browsable(false)] + public XmlDocument Document { + get { + return document; + } + set { + document = value; + + // Update display. + BeginUpdate(); + try { + ShowDocument(); + } finally { + EndUpdate(); + } + } + } + + /// + /// Gets the selected element in the tree. + /// + public XmlElement SelectedElement { + get { + XmlElementTreeNode xmlElementTreeNode = SelectedElementNode; + if (xmlElementTreeNode != null) { + return xmlElementTreeNode.XmlElement; + } + return null; + } + } + + /// + /// Determines whether an element is selected in the tree. + /// + public bool IsElementSelected { + get { + return SelectedElement != null; + } + } + + /// + /// Gets the selected text node in the tree. + /// + public XmlText SelectedTextNode { + get { + XmlTextTreeNode xmlTextTreeNode = SelectedNode as XmlTextTreeNode; + if (xmlTextTreeNode != null) { + return xmlTextTreeNode.XmlText; + } + return null; + } + } + + /// + /// Gets the selected comment node in the tree. + /// + public XmlComment SelectedComment { + get { + XmlCommentTreeNode commentTreeNode = SelectedNode as XmlCommentTreeNode; + if (commentTreeNode != null) { + return commentTreeNode.XmlComment; + } + return null; + } + } + + /// + /// Determines whether a text node is selected in the tree. + /// + public bool IsTextNodeSelected { + get { + return SelectedTextNode != null; + } + } + + /// + /// Saves the current state of the tree. + /// + public void SaveViewState(Properties properties) + { + properties.Set(ViewStatePropertyName, TreeViewHelper.GetViewStateString(this)); + } + + /// + /// Restores the node state of the tree. + /// + public void RestoreViewState(Properties properties) + { + TreeViewHelper.ApplyViewStateString(properties.Get(ViewStatePropertyName, String.Empty), this); + } + + /// + /// Appends a new child element to the currently selected node. + /// + public void AppendChildElement(XmlElement element) + { + XmlElementTreeNode selectedNode = SelectedElementNode; + if (selectedNode != null) { + XmlElementTreeNode newNode = new XmlElementTreeNode(element); + newNode.AddTo(selectedNode); + selectedNode.Expand(); + } + } + + /// + /// Appends a new child text node to the currently selected element. + /// + public void AppendChildTextNode(XmlText textNode) + { + XmlElementTreeNode selectedNode = SelectedElementNode; + if (selectedNode != null) { + XmlTextTreeNode newNode = new XmlTextTreeNode(textNode); + newNode.AddTo(selectedNode); + selectedNode.Expand(); + } + } + + /// + /// Inserts a new element node before the currently selected + /// node. + /// + public void InsertElementBefore(XmlElement element) + { + InsertElement(element, InsertionMode.Before); + } + + /// + /// Inserts a new element node after the currently selected + /// node. + /// + public void InsertElementAfter(XmlElement element) + { + InsertElement(element, InsertionMode.After); + } + + /// + /// Removes the specified element from the tree. + /// + public void RemoveElement(XmlElement element) + { + XmlElementTreeNode node = FindElement(element); + if (node != null) { + node.Remove(); + } + } + + /// + /// Removes the specified text node from the tree. + /// + public void RemoveTextNode(XmlText textNode) + { + XmlTextTreeNode node = FindTextNode(textNode); + if (node != null) { + node.Remove(); + } + } + + /// + /// Inserts a text node before the currently selected + /// node. + /// + public void InsertTextNodeBefore(XmlText textNode) + { + InsertTextNode(textNode, InsertionMode.Before); + } + + /// + /// Inserts a text node after the currently selected + /// node. + /// + public void InsertTextNodeAfter(XmlText textNode) + { + InsertTextNode(textNode, InsertionMode.After); + } + + /// + /// Updates the corresponding tree node's text based on + /// the textNode's value. + /// + public void UpdateTextNode(XmlText textNode) + { + XmlTextTreeNode node = FindTextNode(textNode); + if (node != null) { + node.Update(); + } + } + + /// + /// Updates the corresponding tree node's text based on + /// the comment's value. + /// + public void UpdateComment(XmlComment comment) + { + XmlCommentTreeNode node = FindComment(comment); + if (node != null) { + node.Update(); + } + } + + /// + /// Appends a new child comment node to the currently selected element. + /// + public void AppendChildComment(XmlComment comment) + { + XmlElementTreeNode selectedNode = SelectedElementNode; + if (selectedNode != null) { + XmlCommentTreeNode newNode = new XmlCommentTreeNode(comment); + newNode.AddTo(selectedNode); + selectedNode.Expand(); + } + } + + /// + /// Removes the specified comment from the tree. + /// + public void RemoveComment(XmlComment comment) + { + XmlCommentTreeNode node = FindComment(comment); + if (node != null) { + node.Remove(); + } + } + + /// + /// Inserts a comment node before the currently selected + /// node. + /// + public void InsertCommentBefore(XmlComment comment) + { + InsertComment(comment, InsertionMode.Before); + } + + /// + /// Inserts a comment node after the currently selected + /// node. + /// + public void InsertCommentAfter(XmlComment comment) + { + InsertComment(comment, InsertionMode.After); + } + + /// + /// Updates the image so the corresponding tree node shows that + /// it is in the process of being cut. + /// + public void ShowCut(XmlNode node) + { + ShowCut(node, true); + } + + /// + /// Updates the image so the corresponding tree node no longer + /// shows it is in the process of being cut. + /// + public void HideCut(XmlNode node) + { + ShowCut(node, false); + } + + /// + /// If no node is selected after a mouse click then we make + /// sure the AfterSelect event is fired. Standard behaviour is + /// for the AfterSelect event not to be fired when the user + /// deselects all tree nodes. + /// + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (SelectedNode == null) { + this.OnAfterSelect(new TreeViewEventArgs(null, TreeViewAction.ByMouse)); + } + } + + /// + /// Raises the DeleteKeyPressed event. + /// + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if (keyData == Keys.Delete && DeleteKeyPressed != null) { + DeleteKeyPressed(this, new EventArgs()); + } + return base.ProcessCmdKey(ref msg, keyData); + } + + /// + /// Displays the document in the xml tree. + /// + void ShowDocument() + { + Nodes.Clear(); + if (document != null) { + foreach (XmlNode node in document.ChildNodes) { + switch (node.NodeType) { + case XmlNodeType.Element: + XmlElementTreeNode elementNode = new XmlElementTreeNode((XmlElement)node); + elementNode.AddTo(this); + break; + case XmlNodeType.Comment: + XmlCommentTreeNode commentNode = new XmlCommentTreeNode((XmlComment)node); + commentNode.AddTo(this); + break; + } + } + } + } + + /// + /// Returns the selected xml element tree node. + /// + XmlElementTreeNode SelectedElementNode { + get { + return SelectedNode as XmlElementTreeNode; + } + } + + /// + /// Inserts a new element node either before or after the + /// currently selected element node. + /// + void InsertElement(XmlElement element, InsertionMode insertionMode) + { + ExtTreeNode selectedNode = (ExtTreeNode)SelectedNode; + if (selectedNode != null) { + XmlElementTreeNode parentNode = (XmlElementTreeNode)selectedNode.Parent; + XmlElementTreeNode newNode = new XmlElementTreeNode(element); + int index = parentNode.Nodes.IndexOf(selectedNode); + if (insertionMode == InsertionMode.After) { + index++; + } + newNode.Insert(index, parentNode); + } + } + + /// + /// Inserts a new text node either before or after the + /// currently selected node. + /// + void InsertTextNode(XmlText textNode, InsertionMode insertionMode) + { + ExtTreeNode selectedNode = (ExtTreeNode)SelectedNode; + if (selectedNode != null) { + XmlElementTreeNode parentNode = (XmlElementTreeNode)selectedNode.Parent; + XmlTextTreeNode newNode = new XmlTextTreeNode(textNode); + int index = parentNode.Nodes.IndexOf(selectedNode); + if (insertionMode == InsertionMode.After) { + index++; + } + newNode.Insert(index, parentNode); + } + } + + /// + /// Inserts a new comment node either before or after the + /// currently selected node. + /// + void InsertComment(XmlComment comment, InsertionMode insertionMode) + { + ExtTreeNode selectedNode = (ExtTreeNode)SelectedNode; + if (selectedNode != null) { + ExtTreeNode parentNode = (ExtTreeNode)selectedNode.Parent; + XmlCommentTreeNode newNode = new XmlCommentTreeNode(comment); + int index = 0; + if (parentNode != null) { + index = parentNode.Nodes.IndexOf(selectedNode); + } else { + index = Nodes.IndexOf(selectedNode); + } + if (insertionMode == InsertionMode.After) { + index++; + } + if (parentNode != null) { + newNode.Insert(index, parentNode); + } else { + newNode.Insert(index, this); + } + } + } + + /// + /// Looks at all the nodes in the tree view and returns the + /// tree node that represents the specified element. + /// + XmlElementTreeNode FindElement(XmlElement element, TreeNodeCollection nodes) + { + foreach (ExtTreeNode node in nodes) { + XmlElementTreeNode elementTreeNode = node as XmlElementTreeNode; + if (elementTreeNode != null) { + if (elementTreeNode.XmlElement == element) { + return elementTreeNode; + } + + // Look for a match in the element's child nodes. + XmlElementTreeNode childElementTreeNode = FindElement(element, elementTreeNode.Nodes); + if (childElementTreeNode != null) { + return childElementTreeNode; + } + } + } + return null; + } + + /// + /// Finds the corresponding XmlElementTreeNode. + /// + XmlElementTreeNode FindElement(XmlElement element) + { + XmlElementTreeNode selectedElementTreeNode = SelectedNode as XmlElementTreeNode; + if (selectedElementTreeNode != null && selectedElementTreeNode.XmlElement == element) { + return selectedElementTreeNode; + } else { + return FindElement(element, Nodes); + } + } + + /// + /// Looks at all the nodes in the tree view and returns the + /// tree node that represents the specified text node. + /// + XmlTextTreeNode FindTextNode(XmlText textNode, TreeNodeCollection nodes) + { + foreach (ExtTreeNode node in nodes) { + XmlTextTreeNode textTreeNode = node as XmlTextTreeNode; + if (textTreeNode != null) { + if (textTreeNode.XmlText == textNode) { + return textTreeNode; + } + } else { + // Look for a match in the node's child nodes. + XmlTextTreeNode childTextTreeNode = FindTextNode(textNode, node.Nodes); + if (childTextTreeNode != null) { + return childTextTreeNode; + } + } + } + return null; + } + + /// + /// Finds the specified text node in the tree. + /// + XmlTextTreeNode FindTextNode(XmlText textNode) + { + XmlTextTreeNode selectedTextTreeNode = SelectedNode as XmlTextTreeNode; + if (selectedTextTreeNode != null && selectedTextTreeNode.XmlText == textNode) { + return selectedTextTreeNode; + } else { + return FindTextNode(textNode, Nodes); + } + } + + /// + /// Looks at all the nodes in the tree view and returns the + /// tree node that represents the specified comment node. + /// + XmlCommentTreeNode FindComment(XmlComment comment, TreeNodeCollection nodes) + { + foreach (ExtTreeNode node in nodes) { + XmlCommentTreeNode commentTreeNode = node as XmlCommentTreeNode; + if (commentTreeNode != null) { + if (commentTreeNode.XmlComment == comment) { + return commentTreeNode; + } + } else { + // Look for a match in the node's child nodes. + XmlCommentTreeNode childCommentTreeNode = FindComment(comment, node.Nodes); + if (childCommentTreeNode != null) { + return childCommentTreeNode; + } + } + } + return null; + } + + /// + /// Locates the specified comment in the tree. + /// + XmlCommentTreeNode FindComment(XmlComment comment) + { + XmlCommentTreeNode selectedCommentTreeNode = SelectedNode as XmlCommentTreeNode; + if (selectedCommentTreeNode != null && selectedCommentTreeNode.XmlComment == comment) { + return selectedCommentTreeNode; + } else { + return FindComment(comment, Nodes); + } + } + + /// + /// Shows the corresponding tree node with the ghosted image + /// that indicates it is being cut. + /// + void ShowCutElement(XmlElement element, bool showGhostImage) + { + XmlElementTreeNode node = FindElement(element); + node.ShowGhostImage = showGhostImage; + } + + /// + /// Shows the corresponding tree node with the ghosted image + /// that indicates it is being cut. + /// + void ShowCutTextNode(XmlText textNode, bool showGhostImage) + { + XmlTextTreeNode node = FindTextNode(textNode); + node.ShowGhostImage = showGhostImage; + } + + /// + /// Shows the corresponding tree node with the ghosted image + /// that indicates it is being cut. + /// + void ShowCutComment(XmlComment comment, bool showGhostImage) + { + XmlCommentTreeNode node = FindComment(comment); + node.ShowGhostImage = showGhostImage; + } + + /// + /// Shows the cut node with a ghost image. + /// + /// True if the node should be + /// shown with the ghost image. + void ShowCut(XmlNode node, bool showGhostImage) + { + if (node is XmlElement) { + ShowCutElement((XmlElement)node, showGhostImage); + } else if (node is XmlText) { + ShowCutTextNode((XmlText)node, showGhostImage); + } else if (node is XmlComment) { + ShowCutComment((XmlComment)node, showGhostImage); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/StylesheetAssignedCondition.cs b/src/AddIns/BackendBindings/XmlBinding/Src/StylesheetAssignedCondition.cs new file mode 100644 index 0000000000..40735bee9f --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/StylesheetAssignedCondition.cs @@ -0,0 +1,33 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Determines whether the active XML document has been assigned + /// an XSLT stylesheet. + /// + public class StylesheetAssignedCondition : IConditionEvaluator + { + public bool IsValid(object caller, Condition condition) + { + throw new NotImplementedException(); +// IWorkbenchWindow window = WorkbenchSingleton.Workbench.ActiveWorkbenchWindow; +// if (window != null) { +// XmlView view = window.ActiveViewContent as XmlView; +// if (view != null) { +// return view.StylesheetFileName != null; +// } +// } + return false; + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Xml/EncodedStringWriter.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/EncodedStringWriter.cs new file mode 100644 index 0000000000..647437a742 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/EncodedStringWriter.cs @@ -0,0 +1,48 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.IO; +using System.Text; + +namespace ICSharpCode.XmlEditor +{ + /// + /// A string writer that allows you to specify the text encoding to + /// be used when generating the string. + /// + /// + /// This class is used when generating xml strings using a writer and + /// the encoding in the xml processing instruction needs to be changed. + /// The xml encoding string will be the encoding specified in the constructor + /// of this class (i.e. UTF-8, UTF-16) + public class EncodedStringWriter : StringWriter + { + Encoding encoding = Encoding.UTF8; + + /// + /// Creates a new string writer that will generate a string with the + /// specified encoding. + /// + /// The encoding will be used when generating the + /// xml encoding header (i.e. UTF-8, UTF-16). + public EncodedStringWriter(Encoding encoding) + { + this.encoding = encoding; + } + + /// + /// Gets the text encoding that will be used when generating + /// the string. + /// + public override Encoding Encoding { + get { + return encoding; + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlEncoder.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlEncoder.cs new file mode 100644 index 0000000000..c26764e3d8 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlEncoder.cs @@ -0,0 +1,92 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Text; +using System.Xml; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Utility class that will encode special XML characters. + /// + public sealed class XmlEncoder + { + XmlEncoder() + { + } + + /// + /// Encodes any special characters in the xml string. + /// + public static string Encode(string xml, char quoteCharacter) + { + XmlEncoderTextWriter encoderWriter = new XmlEncoderTextWriter(); + using (XmlTextWriter writer = new XmlTextWriter(encoderWriter)) { + writer.WriteStartElement("root"); + writer.WriteStartAttribute("attribute"); + writer.QuoteChar = quoteCharacter; + + encoderWriter.BeginMarkup(); + writer.WriteString(xml); + return encoderWriter.Markup; + } + } + + /// + /// Special XmlTextWriter that will return the last item written to + /// it from a certain point. This is used by the XmlEncoder to + /// get the encoded attribute string so the XmlEncoder does not + /// have to do the special character encoding itself, but can + /// use the .NET framework to do the work. + /// + class XmlEncoderTextWriter : EncodedStringWriter + { + StringBuilder markup = new StringBuilder(); + + public XmlEncoderTextWriter() : base(Encoding.UTF8) + { + } + + /// + /// Sets the point from which we are interested in + /// saving the string written to the text writer. + /// + public void BeginMarkup() + { + markup = new StringBuilder(); + } + + public void EndMarkup() + { + BeginMarkup(); + } + + /// + /// Returns the string written to this text writer after the + /// BeginMarkup method was called. + /// + public string Markup { + get { + return markup.ToString(); + } + } + + public override void Write(string text) + { + base.Write(text); + markup.Append(text); + } + + public override void Write(char value) + { + base.Write(value); + markup.Append(value); + } + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlNamespace.cs b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlNamespace.cs new file mode 100644 index 0000000000..88c86c78d1 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/Xml/XmlNamespace.cs @@ -0,0 +1,66 @@ +// +// +// +// +// $Revision: 1662 $ +// + +using System; + +namespace ICSharpCode.XmlEditor +{ + /// + /// A namespace Uri and a prefix. + /// + public class XmlNamespace + { + string prefix = String.Empty; + string uri = String.Empty; + + const string prefixToStringStart = "Prefix ["; + const string uriToStringMiddle = "] Uri ["; + + public XmlNamespace(string prefix, string uri) + { + this.prefix = prefix; + this.uri = uri; + } + + public string Prefix { + get { + return prefix; + } + } + + public string Uri { + get { + return uri; + } + } + + public override string ToString() + { + return String.Concat(prefixToStringStart, prefix, uriToStringMiddle, uri, "]"); + } + + /// + /// Creates an XmlNamespace instance from the given string that is in the + /// format returned by ToString. + /// + public static XmlNamespace FromString(string s) + { + int prefixIndex = s.IndexOf(prefixToStringStart); + if (prefixIndex >= 0) { + prefixIndex += prefixToStringStart.Length; + int uriIndex = s.IndexOf(uriToStringMiddle, prefixIndex); + if (uriIndex >= 0) { + string prefix = s.Substring(prefixIndex, uriIndex - prefixIndex); + uriIndex += uriToStringMiddle.Length; + string uri = s.Substring(uriIndex, s.Length - (uriIndex + 1)); + return new XmlNamespace(prefix, uri); + } + } + return new XmlNamespace(String.Empty, String.Empty); + } + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/XmlFoldingStrategy.cs b/src/AddIns/BackendBindings/XmlBinding/Src/XmlFoldingStrategy.cs new file mode 100644 index 0000000000..6602682afd --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/XmlFoldingStrategy.cs @@ -0,0 +1,266 @@ +// +// +// +// +// $Revision: -1 $ +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace ICSharpCode.XmlEditor +{ + /// + /// Holds information about the start of a fold in an xml string. + /// + public class XmlFoldStart + { + int line = 0; + int col = 0; + string prefix = String.Empty; + string name = String.Empty; + string foldText = String.Empty; + + public XmlFoldStart(string prefix, string name, int line, int col) + { + this.line = line; + this.col = col; + this.prefix = prefix; + this.name = name; + } + + /// + /// The line where the fold should start. Lines start from 0. + /// + public int Line { + get { + return line; + } + } + + /// + /// The column where the fold should start. Columns start from 0. + /// + public int Column { + get { + return col; + } + } + + /// + /// The name of the xml item with its prefix if it has one. + /// + public string Name { + get { + if (prefix.Length > 0) { + return String.Concat(prefix, ":", name); + } else { + return name; + } + } + } + + /// + /// The text to be displayed when the item is folded. + /// + public string FoldText { + get { + return foldText; + } + + set { + foldText = value; + } + } + } + + /* + /// + /// Determines folds for an xml string in the editor. + /// + public class XmlFoldingStrategy : IFoldingStrategy + { + /// + /// Flag indicating whether attributes should be displayed on folded + /// elements. + /// + bool showAttributesWhenFolded = false; + + public XmlFoldingStrategy() + { + } + + #region IFoldingStrategy + + /// + /// Adds folds to the text editor around each start-end element pair. + /// + /// + /// If the xml is not well formed then no folds are created. + /// Note that the xml text reader lines and positions start + /// from 1 and the SharpDevelop text editor line information starts + /// from 0. + /// + public List GenerateFoldMarkers(IDocument document, string fileName, object parseInformation) + { + showAttributesWhenFolded = XmlEditorAddInOptions.ShowAttributesWhenFolded; + + List foldMarkers = new List(); + Stack stack = new Stack(); + + try { + string xml = document.TextContent; + XmlTextReader reader = new XmlTextReader(new StringReader(xml)); + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.Element: + if (!reader.IsEmptyElement) { + XmlFoldStart newFoldStart = CreateElementFoldStart(reader); + stack.Push(newFoldStart); + } + break; + + case XmlNodeType.EndElement: + XmlFoldStart foldStart = (XmlFoldStart)stack.Pop(); + CreateElementFold(document, foldMarkers, reader, foldStart); + break; + + case XmlNodeType.Comment: + CreateCommentFold(document, foldMarkers, reader); + break; + } + } + } catch (Exception) { + // If the xml is not well formed keep the foldings + // that already exist in the document. + return new List(document.FoldingManager.FoldMarker); + } + + return foldMarkers; + } + + #endregion + + /// + /// Creates a comment fold if the comment spans more than one line. + /// + /// The text displayed when the comment is folded is the first + /// line of the comment. + void CreateCommentFold(IDocument document, List foldMarkers, XmlTextReader reader) + { + if (reader.Value != null) { + string comment = reader.Value.Replace("\r\n", "\n"); + string[] lines = comment.Split('\n'); + if (lines.Length > 1) { + + // Take off 5 chars to get the actual comment start (takes + // into account the ' + int endCol = lines[lines.Length - 1].Length + startCol + 3; + int endLine = startLine + lines.Length - 1; + string foldText = String.Concat(""); + FoldMarker foldMarker = new FoldMarker(document, startLine, startCol, endLine, endCol, FoldType.TypeBody, foldText); + foldMarkers.Add(foldMarker); + } + } + } + + /// + /// Creates an XmlFoldStart for the start tag of an element. + /// + XmlFoldStart CreateElementFoldStart(XmlTextReader reader) + { + // Take off 2 from the line position returned + // from the xml since it points to the start + // of the element name and not the beginning + // tag. + XmlFoldStart newFoldStart = new XmlFoldStart(reader.Prefix, reader.LocalName, reader.LineNumber - 1, reader.LinePosition - 2); + + if (showAttributesWhenFolded && reader.HasAttributes) { + newFoldStart.FoldText = String.Concat("<", newFoldStart.Name, " ", GetAttributeFoldText(reader), ">"); + } else { + newFoldStart.FoldText = String.Concat("<", newFoldStart.Name, ">"); + } + + return newFoldStart; + } + + /// + /// Create an element fold if the start and end tag are on + /// different lines. + /// + void CreateElementFold(IDocument document, List foldMarkers, XmlTextReader reader, XmlFoldStart foldStart) + { + int endLine = reader.LineNumber - 1; + if (endLine > foldStart.Line) { + int endCol = reader.LinePosition + foldStart.Name.Length; + FoldMarker foldMarker = new FoldMarker(document, foldStart.Line, foldStart.Column, endLine, endCol, FoldType.TypeBody, foldStart.FoldText); + foldMarkers.Add(foldMarker); + } + } + + /// + /// Gets the element's attributes as a string on one line that will + /// be displayed when the element is folded. + /// + /// + /// Currently this puts all attributes from an element on the same + /// line of the start tag. It does not cater for elements where attributes + /// are not on the same line as the start tag. + /// + string GetAttributeFoldText(XmlTextReader reader) + { + StringBuilder text = new StringBuilder(); + + for (int i = 0; i < reader.AttributeCount; ++i) { + reader.MoveToAttribute(i); + + text.Append(reader.Name); + text.Append("="); + text.Append(reader.QuoteChar.ToString()); + text.Append(XmlEncodeAttributeValue(reader.Value, reader.QuoteChar)); + text.Append(reader.QuoteChar.ToString()); + + // Append a space if this is not the + // last attribute. + if (i < reader.AttributeCount - 1) { + text.Append(" "); + } + } + + return text.ToString(); + } + + /// + /// Xml encode the attribute string since the string returned from + /// the XmlTextReader is the plain unencoded string and .NET + /// does not provide us with an xml encode method. + /// + static string XmlEncodeAttributeValue(string attributeValue, char quoteChar) + { + StringBuilder encodedValue = new StringBuilder(attributeValue); + + encodedValue.Replace("&", "&"); + encodedValue.Replace("<", "<"); + encodedValue.Replace(">", ">"); + + if (quoteChar == '"') { + encodedValue.Replace("\"", """); + } else { + encodedValue.Replace("'", "'"); + } + + return encodedValue.ToString(); + } + } + + */ +} diff --git a/src/AddIns/BackendBindings/XmlBinding/Src/XmlFormattingStrategy.cs b/src/AddIns/BackendBindings/XmlBinding/Src/XmlFormattingStrategy.cs new file mode 100644 index 0000000000..976b05eaa9 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/Src/XmlFormattingStrategy.cs @@ -0,0 +1,189 @@ +// +// +// +// +// $Revision: -1 $ +// + +using ICSharpCode.SharpDevelop.Editor; +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Xml; +using ICSharpCode.Core; + +namespace ICSharpCode.XmlBinding +{ + /// + /// This class currently inserts the closing tags to typed openening tags + /// and does smart indentation for xml files. + /// + public class XmlFormattingStrategy : DefaultFormattingStrategy + { + public override void FormatLine(ITextEditor editor, char charTyped) + { + editor.Document.StartUndoableAction(); + try { + if (charTyped == '>') { + StringBuilder stringBuilder = new StringBuilder(); + int offset = Math.Min(editor.Caret.Offset - 2, editor.Document.TextLength - 1); + while (true) { + if (offset < 0) { + break; + } + char ch = editor.Document.GetCharAt(offset); + if (ch == '<') { + string reversedTag = stringBuilder.ToString().Trim(); + if (!reversedTag.StartsWith("/") && !reversedTag.EndsWith("/")) { + bool validXml = true; + try { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(editor.Document.Text); + } catch (Exception) { + validXml = false; + } + // only insert the tag, if something is missing + if (!validXml) { + StringBuilder tag = new StringBuilder(); + for (int i = reversedTag.Length - 1; i >= 0 && !Char.IsWhiteSpace(reversedTag[i]); --i) { + tag.Append(reversedTag[i]); + } + string tagString = tag.ToString(); + if (tagString.Length > 0 && !tagString.StartsWith("!") && !tagString.StartsWith("?")) { + editor.Document.Insert(editor.Caret.Offset, ""); + } + } + } + break; + } + stringBuilder.Append(ch); + --offset; + } + } + } catch (Exception e) { // Insanity check + Debug.Assert(false, e.ToString()); + } + if (charTyped == '\n') { + IndentLine(editor, editor.Document.GetLineForOffset(editor.Caret.Offset)); + } + editor.Document.EndUndoableAction(); + } + + public override void IndentLine(ITextEditor editor, IDocumentLine line) + { + editor.Document.StartUndoableAction(); + try { + TryIndent(editor, line.LineNumber, line.LineNumber); + } catch (XmlException ex) { + LoggingService.Debug(ex.ToString()); + } finally { + editor.Document.EndUndoableAction(); + } + } + + /// + /// This function sets the indentlevel in a range of lines. + /// + public override void IndentLines(ITextEditor editor, int begin, int end) + { + editor.Document.StartUndoableAction(); + try { + TryIndent(editor, begin, end); + } catch (XmlException ex) { + LoggingService.Debug(ex.ToString()); + } finally { + editor.Document.EndUndoableAction(); + } + } + #region Smart Indentation + private void TryIndent(ITextEditor editor, int begin, int end) + { + string currentIndentation = ""; + Stack tagStack = new Stack(); + IDocument document = editor.Document; + string tab = editor.Options.IndentationString; + int nextLine = begin; // in #dev coordinates + bool wasEmptyElement = false; + XmlNodeType lastType = XmlNodeType.XmlDeclaration; + using (StringReader stringReader = new StringReader(document.Text)) { + XmlTextReader r = new XmlTextReader(stringReader); + r.XmlResolver = null; // prevent XmlTextReader from loading external DTDs + while (r.Read()) { + if (wasEmptyElement) { + wasEmptyElement = false; + if (tagStack.Count == 0) + currentIndentation = ""; + else + currentIndentation = (string)tagStack.Pop(); + } + if (r.NodeType == XmlNodeType.EndElement) { + if (tagStack.Count == 0) + currentIndentation = ""; + else + currentIndentation = (string)tagStack.Pop(); + } + + while (r.LineNumber >= nextLine) { // caution: here we compare 1-based and 0-based line numbers + if (nextLine > end) break; + if (lastType == XmlNodeType.CDATA || lastType == XmlNodeType.Comment) { + nextLine++; + continue; + } + // set indentation of 'nextLine' + IDocumentLine line = document.GetLine(nextLine); + string lineText = line.Text; + + string newText; + // special case: opening tag has closing bracket on extra line: remove one indentation level + if (lineText.Trim() == ">") + newText = (string)tagStack.Peek() + lineText.Trim(); + else + newText = currentIndentation + lineText.Trim(); + + if (newText != lineText) { + document.Replace(line.Offset, line.Length, newText); + } + nextLine++; + } + if (r.LineNumber > end) + break; + wasEmptyElement = r.NodeType == XmlNodeType.Element && r.IsEmptyElement; + string attribIndent = null; + if (r.NodeType == XmlNodeType.Element) { + tagStack.Push(currentIndentation); + if (r.LineNumber < begin) + currentIndentation = DocumentUtilitites.GetIndentation(editor.Document, r.LineNumber - 1); + if (r.Name.Length < 16) + attribIndent = currentIndentation + new String(' ', 2 + r.Name.Length); + else + attribIndent = currentIndentation + tab; + currentIndentation += tab; + } + lastType = r.NodeType; + if (r.NodeType == XmlNodeType.Element && r.HasAttributes) { + int startLine = r.LineNumber; + r.MoveToAttribute(0); // move to first attribute + if (r.LineNumber != startLine) + attribIndent = currentIndentation; // change to tab-indentation + r.MoveToAttribute(r.AttributeCount - 1); + while (r.LineNumber >= nextLine) { + if (nextLine > end) break; + // set indentation of 'nextLine' + IDocumentLine line = document.GetLine(nextLine); + string lineText = line.Text; + string newText = attribIndent + lineText.Trim(); + if (newText != lineText) { + document.Replace(line.Offset, line.Length, newText); + } + nextLine++; + } + } + } + r.Close(); + } + } + #endregion + } +} diff --git a/src/AddIns/BackendBindings/XmlBinding/XmlBinding.addin b/src/AddIns/BackendBindings/XmlBinding/XmlBinding.addin new file mode 100644 index 0000000000..fecea15ef0 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/XmlBinding.addin @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AddIns/BackendBindings/XmlBinding/XmlBinding.csproj b/src/AddIns/BackendBindings/XmlBinding/XmlBinding.csproj new file mode 100644 index 0000000000..e854e657c2 --- /dev/null +++ b/src/AddIns/BackendBindings/XmlBinding/XmlBinding.csproj @@ -0,0 +1,159 @@ + + + {DCA2703D-250A-463E-A68A-07ED105AE6BD} + Debug + AnyCPU + Library + ICSharpCode.XmlBinding + XmlBinding + v3.5 + C:\Users\Siegfried\AppData\Roaming\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + False + False + 4 + false + ..\..\..\..\AddIns\AddIns\BackendBindings\XmlBinding\ + + + true + Full + False + True + DEBUG;TRACE + + + False + None + True + False + TRACE + + + False + Auto + 4194304 + x86 + 4096 + + + + + + 3.5 + + + + 3.5 + + + + + + 3.5 + + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + + + + + + + + + + + + + + + + + UserControl + + + + + + + + + + UserControl + + + + + + + + + + + + + + + + + + + + + + + {2748AD25-9C63-4E12-877B-4DCE96FBED54} + ICSharpCode.SharpDevelop + + + {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} + ICSharpCode.Core + + + {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} + ICSharpCode.Core.WinForms + + + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + ICSharpCode.SharpDevelop.Dom + + + \ No newline at end of file