#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

603 lines
16 KiB

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.XmlEditor
{
public class XmlTreeViewKeyPressedEventArgs : EventArgs
{
public XmlTreeViewKeyPressedEventArgs(Keys keyData)
{
KeyData = keyData;
}
public Keys KeyData
{
get;
private set;
}
}
/// <summary>
/// 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.
/// </summary>
public class XmlTreeViewControl : ExtTreeView
{
const string ViewStatePropertyName = "XmlTreeViewControl.ViewState";
XmlDocument document;
enum InsertionMode {
Before = 0,
After = 1
}
/// <summary>
/// Raised when some key in tree view is pressed.
/// </summary>
public event EventHandler<XmlTreeViewKeyPressedEventArgs> TreeViewKeyPressed;
public XmlTreeViewControl()
{
}
/// <summary>
/// Gets or sets the xml document currently being displayed.
/// </summary>
[Browsable(false)]
public XmlDocument Document {
get {
return document;
}
set {
document = value;
// Update display.
BeginUpdate();
try {
ShowDocument();
} finally {
EndUpdate();
}
}
}
/// <summary>
/// Gets the selected element in the tree.
/// </summary>
public XmlElement SelectedElement {
get {
XmlElementTreeNode xmlElementTreeNode = SelectedElementNode;
if (xmlElementTreeNode != null) {
return xmlElementTreeNode.XmlElement;
}
return null;
}
}
/// <summary>
/// Determines whether an element is selected in the tree.
/// </summary>
public bool IsElementSelected {
get {
return SelectedElement != null;
}
}
/// <summary>
/// Gets the selected text node in the tree.
/// </summary>
public XmlText SelectedTextNode {
get {
XmlTextTreeNode xmlTextTreeNode = SelectedNode as XmlTextTreeNode;
if (xmlTextTreeNode != null) {
return xmlTextTreeNode.XmlText;
}
return null;
}
}
/// <summary>
/// Gets the selected comment node in the tree.
/// </summary>
public XmlComment SelectedComment {
get {
XmlCommentTreeNode commentTreeNode = SelectedNode as XmlCommentTreeNode;
if (commentTreeNode != null) {
return commentTreeNode.XmlComment;
}
return null;
}
}
/// <summary>
/// Determines whether a text node is selected in the tree.
/// </summary>
public bool IsTextNodeSelected {
get {
return SelectedTextNode != null;
}
}
/// <summary>
/// Saves the current state of the tree.
/// </summary>
public void SaveViewState(Properties properties)
{
properties.Set(ViewStatePropertyName, TreeViewHelper.GetViewStateString(this));
}
/// <summary>
/// Restores the node state of the tree.
/// </summary>
public void RestoreViewState(Properties properties)
{
TreeViewHelper.ApplyViewStateString(properties.Get(ViewStatePropertyName, string.Empty), this);
}
/// <summary>
/// Appends a new child element to the currently selected node.
/// </summary>
public void AppendChildElement(XmlElement element)
{
XmlElementTreeNode selectedNode = SelectedElementNode;
if (selectedNode != null) {
XmlElementTreeNode newNode = new XmlElementTreeNode(element);
newNode.AddTo(selectedNode);
selectedNode.Expand();
}
}
/// <summary>
/// Appends a new child text node to the currently selected element.
/// </summary>
public void AppendChildTextNode(XmlText textNode)
{
XmlElementTreeNode selectedNode = SelectedElementNode;
if (selectedNode != null) {
XmlTextTreeNode newNode = new XmlTextTreeNode(textNode);
newNode.AddTo(selectedNode);
selectedNode.Expand();
}
}
/// <summary>
/// Inserts a new element node before the currently selected
/// node.
/// </summary>
public void InsertElementBefore(XmlElement element)
{
InsertElement(element, InsertionMode.Before);
}
/// <summary>
/// Inserts a new element node after the currently selected
/// node.
/// </summary>
public void InsertElementAfter(XmlElement element)
{
InsertElement(element, InsertionMode.After);
}
/// <summary>
/// Removes the specified element from the tree.
/// </summary>
public void RemoveElement(XmlElement element)
{
XmlElementTreeNode node = FindElement(element);
if (node != null) {
node.Remove();
}
}
/// <summary>
/// Removes the specified text node from the tree.
/// </summary>
public void RemoveTextNode(XmlText textNode)
{
XmlTextTreeNode node = FindTextNode(textNode);
if (node != null) {
node.Remove();
}
}
/// <summary>
/// Inserts a text node before the currently selected
/// node.
/// </summary>
public void InsertTextNodeBefore(XmlText textNode)
{
InsertTextNode(textNode, InsertionMode.Before);
}
/// <summary>
/// Inserts a text node after the currently selected
/// node.
/// </summary>
public void InsertTextNodeAfter(XmlText textNode)
{
InsertTextNode(textNode, InsertionMode.After);
}
/// <summary>
/// Updates the corresponding tree node's text based on
/// the textNode's value.
/// </summary>
public void UpdateTextNode(XmlText textNode)
{
XmlTextTreeNode node = FindTextNode(textNode);
if (node != null) {
node.Update();
}
}
/// <summary>
/// Updates the corresponding tree node's text based on
/// the comment's value.
/// </summary>
public void UpdateComment(XmlComment comment)
{
XmlCommentTreeNode node = FindComment(comment);
if (node != null) {
node.Update();
}
}
/// <summary>
/// Appends a new child comment node to the currently selected element.
/// </summary>
public void AppendChildComment(XmlComment comment)
{
XmlElementTreeNode selectedNode = SelectedElementNode;
if (selectedNode != null) {
XmlCommentTreeNode newNode = new XmlCommentTreeNode(comment);
newNode.AddTo(selectedNode);
selectedNode.Expand();
}
}
/// <summary>
/// Removes the specified comment from the tree.
/// </summary>
public void RemoveComment(XmlComment comment)
{
XmlCommentTreeNode node = FindComment(comment);
if (node != null) {
node.Remove();
}
}
/// <summary>
/// Inserts a comment node before the currently selected
/// node.
/// </summary>
public void InsertCommentBefore(XmlComment comment)
{
InsertComment(comment, InsertionMode.Before);
}
/// <summary>
/// Inserts a comment node after the currently selected
/// node.
/// </summary>
public void InsertCommentAfter(XmlComment comment)
{
InsertComment(comment, InsertionMode.After);
}
/// <summary>
/// Updates the image so the corresponding tree node shows that
/// it is in the process of being cut.
/// </summary>
public void ShowCut(XmlNode node)
{
ShowCut(node, true);
}
/// <summary>
/// Updates the image so the corresponding tree node no longer
/// shows it is in the process of being cut.
/// </summary>
public void HideCut(XmlNode node)
{
ShowCut(node, false);
}
/// <summary>
/// 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.
/// </summary>
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (SelectedNode == null) {
this.OnAfterSelect(new TreeViewEventArgs(null, TreeViewAction.ByMouse));
}
}
/// <summary>
/// Raises the DeleteKeyPressed event.
/// </summary>
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (/*keyData == Keys.Delete && */TreeViewKeyPressed != null) {
TreeViewKeyPressed(this, new XmlTreeViewKeyPressedEventArgs(keyData));
}
return base.ProcessCmdKey(ref msg, keyData);
}
/// <summary>
/// Displays the document in the xml tree.
/// </summary>
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;
}
}
}
}
/// <summary>
/// Returns the selected xml element tree node.
/// </summary>
XmlElementTreeNode SelectedElementNode {
get {
return SelectedNode as XmlElementTreeNode;
}
}
/// <summary>
/// Inserts a new element node either before or after the
/// currently selected element node.
/// </summary>
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);
}
}
/// <summary>
/// Inserts a new text node either before or after the
/// currently selected node.
/// </summary>
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);
}
}
/// <summary>
/// Inserts a new comment node either before or after the
/// currently selected node.
/// </summary>
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);
}
}
}
/// <summary>
/// Looks at all the nodes in the tree view and returns the
/// tree node that represents the specified element.
/// </summary>
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;
}
/// <summary>
/// Finds the corresponding XmlElementTreeNode.
/// </summary>
XmlElementTreeNode FindElement(XmlElement element)
{
XmlElementTreeNode selectedElementTreeNode = SelectedNode as XmlElementTreeNode;
if (selectedElementTreeNode != null && selectedElementTreeNode.XmlElement == element) {
return selectedElementTreeNode;
} else {
return FindElement(element, Nodes);
}
}
/// <summary>
/// Looks at all the nodes in the tree view and returns the
/// tree node that represents the specified text node.
/// </summary>
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;
}
/// <summary>
/// Finds the specified text node in the tree.
/// </summary>
XmlTextTreeNode FindTextNode(XmlText textNode)
{
XmlTextTreeNode selectedTextTreeNode = SelectedNode as XmlTextTreeNode;
if (selectedTextTreeNode != null && selectedTextTreeNode.XmlText == textNode) {
return selectedTextTreeNode;
} else {
return FindTextNode(textNode, Nodes);
}
}
/// <summary>
/// Looks at all the nodes in the tree view and returns the
/// tree node that represents the specified comment node.
/// </summary>
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;
}
/// <summary>
/// Locates the specified comment in the tree.
/// </summary>
XmlCommentTreeNode FindComment(XmlComment comment)
{
XmlCommentTreeNode selectedCommentTreeNode = SelectedNode as XmlCommentTreeNode;
if (selectedCommentTreeNode != null && selectedCommentTreeNode.XmlComment == comment) {
return selectedCommentTreeNode;
} else {
return FindComment(comment, Nodes);
}
}
/// <summary>
/// Shows the corresponding tree node with the ghosted image
/// that indicates it is being cut.
/// </summary>
void ShowCutElement(XmlElement element, bool showGhostImage)
{
XmlElementTreeNode node = FindElement(element);
node.ShowGhostImage = showGhostImage;
}
/// <summary>
/// Shows the corresponding tree node with the ghosted image
/// that indicates it is being cut.
/// </summary>
void ShowCutTextNode(XmlText textNode, bool showGhostImage)
{
XmlTextTreeNode node = FindTextNode(textNode);
node.ShowGhostImage = showGhostImage;
}
/// <summary>
/// Shows the corresponding tree node with the ghosted image
/// that indicates it is being cut.
/// </summary>
void ShowCutComment(XmlComment comment, bool showGhostImage)
{
XmlCommentTreeNode node = FindComment(comment);
node.ShowGhostImage = showGhostImage;
}
/// <summary>
/// Shows the cut node with a ghost image.
/// </summary>
/// <param name="showGhostImage">True if the node should be
/// shown with the ghost image.</param>
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);
}
}
}
}