From c053b954a67f083554c05ac1588dc703077c8fe7 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Thu, 14 Dec 2006 20:10:19 +0000 Subject: [PATCH] Added basic cut, copy and paste to the XML tree editor. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2164 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../XmlEditor/Project/Src/IXmlTreeView.cs | 18 + .../Project/Src/RemoveCommentCommand.cs | 26 -- .../Project/Src/RemoveElementCommand.cs | 26 -- .../Project/Src/RemoveTextNodeCommand.cs | 26 -- .../Project/Src/XmlCommentTreeNode.cs | 19 + .../XmlEditor/Project/Src/XmlEditorControl.cs | 52 ++- .../Project/Src/XmlElementTreeNode.cs | 21 +- .../XmlEditor/Project/Src/XmlTextTreeNode.cs | 19 + .../XmlEditor/Project/Src/XmlTreeEditor.cs | 324 ++++++++++++--- .../XmlEditor/Project/Src/XmlTreeView.cs | 106 ++++- .../Src/XmlTreeViewContainerControl.cs | 175 ++++++-- .../Project/Src/XmlTreeViewControl.cs | 80 ++++ .../XmlEditor/Project/Src/XmlView.cs | 66 +++- .../XmlEditor/Project/XmlEditor.addin | 41 +- .../XmlEditor/Project/XmlEditor.csproj | 3 - .../DeleteTreeNodeWithDeleteKeyTestFixture.cs | 39 ++ ...ditCommentNodesInTreeControlTestFixture.cs | 11 + .../Test/Tree/EditCommentNodesTestFixture.cs | 6 +- .../Test/Tree/MenuCommandsTestFixture.cs | 53 +-- .../Tree/PasteInTreeControlTestFixture.cs | 276 +++++++++++++ .../XmlEditor/Test/Tree/PasteTestFixture.cs | 372 ++++++++++++++++++ .../Test/Tree/RemoveElementTestFixture.cs | 6 +- .../Test/Tree/RemoveTextNodeTestFixture.cs | 6 +- .../Test/Tree/XmlCommentTreeNodeTests.cs | 16 + .../Test/Tree/XmlTextTreeNodeTextTests.cs | 16 + .../XmlTreeViewClipboardHandlerTestFixture.cs | 133 +++++++ .../Tree/XmlTreeViewContainerTestFixture.cs | 52 ++- .../Test/Utils/DerivedXmlTreeView.cs | 6 + .../DerivedXmlTreeViewContainerControl.cs | 10 + .../XmlEditor/Test/Utils/MockXmlTreeView.cs | 47 ++- .../XmlEditor/Test/XmlEditor.Tests.csproj | 4 + .../DisplayBindings/XmlEditor/XmlEditor.sln | 2 +- .../Base/Project/Src/Services/IconService.cs | 7 +- 33 files changed, 1783 insertions(+), 281 deletions(-) delete mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveCommentCommand.cs delete mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveElementCommand.cs delete mode 100644 src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveTextNodeCommand.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Test/Tree/DeleteTreeNodeWithDeleteKeyTestFixture.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteInTreeControlTestFixture.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteTestFixture.cs create mode 100644 src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewClipboardHandlerTestFixture.cs diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/IXmlTreeView.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/IXmlTreeView.cs index 628b82c50e..c025a56056 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/IXmlTreeView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/IXmlTreeView.cs @@ -30,6 +30,11 @@ namespace ICSharpCode.XmlEditor /// XmlDocument Document {get; set;} + /// + /// Gets the selected node in the tree. + /// + XmlNode SelectedNode {get;} + /// /// Gets the xml element selected. /// @@ -112,6 +117,19 @@ namespace ICSharpCode.XmlEditor /// 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. diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveCommentCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveCommentCommand.cs deleted file mode 100644 index c5282327ee..0000000000 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveCommentCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// -// -// -// $Revision$ -// - -using System; -using ICSharpCode.Core; - -namespace ICSharpCode.XmlEditor -{ - /// - /// Removes the currently selected comment node from the XML tree. - /// - public class RemoveCommentCommand : AbstractMenuCommand - { - public override void Run() - { - XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; - if (view != null) { - view.RemoveComment(); - } - } - } -} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveElementCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveElementCommand.cs deleted file mode 100644 index 456420bb8a..0000000000 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveElementCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// -// -// -// $Revision$ -// - -using System; -using ICSharpCode.Core; - -namespace ICSharpCode.XmlEditor -{ - /// - /// Removes the selected element from the XML tree. - /// - public class RemoveElementCommand : AbstractMenuCommand - { - public override void Run() - { - XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; - if (view != null) { - view.RemoveElement(); - } - } - } -} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveTextNodeCommand.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveTextNodeCommand.cs deleted file mode 100644 index de441eedbd..0000000000 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/RemoveTextNodeCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// -// -// -// $Revision$ -// - -using System; -using ICSharpCode.Core; - -namespace ICSharpCode.XmlEditor -{ - /// - /// Removes the currently selected text node from the XML tree. - /// - public class RemoveTextNodeCommand : AbstractMenuCommand - { - public override void Run() - { - XmlTreeViewContainerControl view = Owner as XmlTreeViewContainerControl; - if (view != null) { - view.RemoveTextNode(); - } - } - } -} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCommentTreeNode.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCommentTreeNode.cs index cb904f143f..1d798c4af9 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCommentTreeNode.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCommentTreeNode.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.XmlEditor public class XmlCommentTreeNode : XmlCharacterDataTreeNode { public const string XmlCommentTreeNodeImageKey = "XmlCommentTreeNodeImage"; + public const string XmlCommentTreeNodeGhostImageKey = "XmlCommentTreeNodeGhostImage"; XmlComment comment; @@ -37,5 +38,23 @@ namespace ICSharpCode.XmlEditor 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/DisplayBindings/XmlEditor/Project/Src/XmlEditorControl.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlEditorControl.cs index c499787a8d..785aebb0c8 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlEditorControl.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlEditorControl.cs @@ -23,12 +23,12 @@ namespace ICSharpCode.XmlEditor /// public class XmlEditorControl : ICSharpCode.TextEditor.TextEditorControl { - static readonly string editActionsPath = "/AddIns/XmlEditor/EditActions"; - static readonly string contextMenuPath = "/SharpDevelop/ViewContent/XmlEditor/ContextMenu"; CodeCompletionWindow codeCompletionWindow; XmlSchemaCompletionDataCollection schemaCompletionDataItems = new XmlSchemaCompletionDataCollection(); XmlSchemaCompletionData defaultSchemaCompletionData = null; string defaultNamespacePrefix = String.Empty; + ContextMenuStrip contextMenuStrip; + TextAreaControl primaryTextAreaControl; public XmlEditorControl() { @@ -37,13 +37,10 @@ namespace ICSharpCode.XmlEditor Document.HighlightingStrategy = HighlightingManager.Manager.FindHighlighter("XML"); Document.FoldingManager.FoldingStrategy = new XmlFoldingStrategy(); - TextEditorProperties = new SharpDevelopTextEditorProperties(); Document.BookmarkManager.Factory = new SDBookmarkFactory(Document.BookmarkManager); Document.BookmarkManager.Added += new ICSharpCode.TextEditor.Document.BookmarkEventHandler(BookmarkAdded); Document.BookmarkManager.Removed += new ICSharpCode.TextEditor.Document.BookmarkEventHandler(BookmarkRemoved); - - GenerateEditActions(); } /// @@ -83,7 +80,6 @@ namespace ICSharpCode.XmlEditor get { return defaultSchemaCompletionData; } - set { defaultSchemaCompletionData = value; } @@ -103,12 +99,43 @@ namespace ICSharpCode.XmlEditor } } + /// + /// Adds edit actions to the xml editor. + /// + public void AddEditActions(IEditAction[] actions) + { + foreach (IEditAction action in actions) { + foreach (Keys key in action.Keys) { + editactions[key] = action; + } + } + } + + /// + /// Gets or sets the right click menu associated with the + /// xml editor. + /// + public ContextMenuStrip TextAreaContextMenuStrip { + get { + return contextMenuStrip; + } + set { + contextMenuStrip = value; + if (primaryTextAreaControl != null) { + primaryTextAreaControl.ContextMenuStrip = value; + } + } + } + protected override void InitializeTextAreaControl(TextAreaControl newControl) { base.InitializeTextAreaControl(newControl); + + primaryTextAreaControl = newControl; + newControl.TextArea.KeyEventHandler += new ICSharpCode.TextEditor.KeyEventHandler(HandleKeyPress); - newControl.ContextMenuStrip = MenuService.CreateContextMenu(this, contextMenuPath); + newControl.ContextMenuStrip = contextMenuStrip; newControl.SelectionManager.SelectionChanged += new EventHandler(SelectionChanged); newControl.Document.DocumentChanged += new DocumentEventHandler(DocumentChanged); newControl.TextArea.ClipboardHandler.CopyText += new CopyTextEventHandler(ClipboardHandlerCopyText); @@ -202,17 +229,6 @@ namespace ICSharpCode.XmlEditor } } - void GenerateEditActions() - { - IEditAction[] actions = (IEditAction[])(AddInTree.BuildItems(editActionsPath, this, false).ToArray(typeof(IEditAction))); - - foreach (IEditAction action in actions) { - foreach (Keys key in action.Keys) { - editactions[key] = action; - } - } - } - void DocumentChanged(object sender, DocumentEventArgs e) { } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlElementTreeNode.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlElementTreeNode.cs index d5598230f9..0ac741f95f 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlElementTreeNode.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlElementTreeNode.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.XmlEditor public class XmlElementTreeNode : ExtTreeNode { public const string XmlElementTreeNodeImageKey = "XmlElementTreeNodeImage"; + public const string XmlElementTreeNodeGhostImageKey = "XmlElementTreeNodeGhostImage"; XmlElement element; @@ -41,7 +42,25 @@ namespace ICSharpCode.XmlEditor 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. diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTextTreeNode.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTextTreeNode.cs index 4ea6808176..d253e7b7d6 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTextTreeNode.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTextTreeNode.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.XmlEditor public class XmlTextTreeNode : XmlCharacterDataTreeNode { public const string XmlTextTreeNodeImageKey = "XmlTextTreeNodeImage"; + public const string XmlTextTreeNodeGhostImageKey = "XmlTextTreeNodeGhostImage"; XmlText xmlText; @@ -37,5 +38,23 @@ namespace ICSharpCode.XmlEditor 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/DisplayBindings/XmlEditor/Project/Src/XmlTreeEditor.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeEditor.cs index 5c5c42b834..47f3c90d96 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeEditor.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeEditor.cs @@ -22,6 +22,8 @@ namespace ICSharpCode.XmlEditor IXmlTreeView view; XmlDocument document; XmlCompletionDataProvider completionDataProvider; + XmlNode copiedNode; + XmlNode cutNode; public XmlTreeEditor(IXmlTreeView view, XmlCompletionDataProvider completionDataProvider) { @@ -149,8 +151,7 @@ namespace ICSharpCode.XmlEditor view.IsDirty = true; foreach (string elementName in selectedElementNames) { XmlElement newElement = document.CreateElement(elementName, selectedElement.NamespaceURI); - selectedElement.AppendChild(newElement); - view.AppendChildElement(newElement); + AppendChildElement(selectedElement, newElement); } } } @@ -212,46 +213,12 @@ namespace ICSharpCode.XmlEditor } } - /// - /// Removes the currently selected element. - /// - public void RemoveElement() - { - XmlElement selectedElement = view.SelectedElement; - if (selectedElement != null) { - XmlNode parentNode = selectedElement.ParentNode; - parentNode.RemoveChild(selectedElement); - view.IsDirty = true; - view.RemoveElement(selectedElement); - } - } - - /// - /// Removes the currently select text node. - /// - public void RemoveTextNode() - { - XmlText textNode = view.SelectedTextNode; - if (textNode != null) { - XmlNode parentNode = textNode.ParentNode; - parentNode.RemoveChild(textNode); - view.IsDirty = true; - view.RemoveTextNode(textNode); - } - } - /// /// Adds a child text node to the current selected element. /// public void AppendChildTextNode() { - XmlElement selectedElement = view.SelectedElement; - if (selectedElement != null) { - XmlText textNode = document.CreateTextNode(String.Empty); - selectedElement.AppendChild(textNode); - view.IsDirty = true; - view.AppendChildTextNode(textNode); - } + AppendChildTextNode(document.CreateTextNode(String.Empty)); } /// @@ -299,27 +266,8 @@ namespace ICSharpCode.XmlEditor /// public void AppendChildComment() { - XmlElement selectedElement = view.SelectedElement; - if (selectedElement != null) { - XmlComment comment = document.CreateComment(String.Empty); - selectedElement.AppendChild(comment); - view.IsDirty = true; - view.AppendChildComment(comment); - } - } - - /// - /// Removes the currently select comment. - /// - public void RemoveComment() - { - XmlComment comment = view.SelectedComment; - if (comment != null) { - XmlNode parentNode = comment.ParentNode; - parentNode.RemoveChild(comment); - view.IsDirty = true; - view.RemoveComment(comment); - } + XmlComment comment = document.CreateComment(String.Empty); + AppendChildComment(comment); } /// @@ -352,6 +300,105 @@ namespace ICSharpCode.XmlEditor } } + /// + /// 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. @@ -436,5 +483,166 @@ namespace ICSharpCode.XmlEditor } 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); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs index 788794ccfa..3c2a662beb 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs @@ -17,21 +17,31 @@ namespace ICSharpCode.XmlEditor /// /// The secondary view content that displays the XML document as a tree view. /// - public class XmlTreeView : AbstractSecondaryViewContent + public class XmlTreeView : AbstractSecondaryViewContent, IClipboardHandler { XmlTreeViewContainerControl treeViewContainer = new XmlTreeViewContainerControl(); XmlView xmlView; bool disposed; bool ignoreDirtyChange; - public XmlTreeView(XmlView xmlView) + public XmlTreeView(XmlView xmlView) : this(xmlView, null, null) { - this.xmlView = xmlView; - treeViewContainer.DirtyChanged += TreeViewContainerDirtyChanged; treeViewContainer.AttributesGrid.ContextMenuStrip = MenuService.CreateContextMenu(treeViewContainer, "/AddIns/XmlEditor/XmlTree/AttributesGrid/ContextMenu"); treeViewContainer.TreeView.ContextMenuStrip = MenuService.CreateContextMenu(treeViewContainer, "/AddIns/XmlEditor/XmlTree/ContextMenu"); } + /// + /// Creates an XmlTreeView with the specified context menu strips. + /// This constructor is only used to test the XmlTreeView class. + /// + public XmlTreeView(XmlView xmlView, ContextMenuStrip attributesGridContextMenuStrip, ContextMenuStrip treeViewContextMenuStrip) + { + this.xmlView = xmlView; + treeViewContainer.DirtyChanged += TreeViewContainerDirtyChanged; + treeViewContainer.AttributesGrid.ContextMenuStrip = attributesGridContextMenuStrip; + treeViewContainer.TreeView.ContextMenuStrip = treeViewContextMenuStrip; + } + public override Control Control { get { return treeViewContainer; @@ -86,6 +96,94 @@ namespace ICSharpCode.XmlEditor } } + #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) { diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewContainerControl.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewContainerControl.cs index 48c677daf3..419ae38041 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewContainerControl.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewContainerControl.cs @@ -12,6 +12,8 @@ using System.Windows.Forms; using System.Xml; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.XmlEditor { @@ -20,7 +22,7 @@ namespace ICSharpCode.XmlEditor /// 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 + public class XmlTreeViewContainerControl : System.Windows.Forms.UserControl, IXmlTreeView, IOwnerState, IClipboardHandler { XmlTreeEditor editor; bool dirty; @@ -216,6 +218,26 @@ namespace ICSharpCode.XmlEditor } } + /// + /// 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. /// @@ -357,14 +379,6 @@ namespace ICSharpCode.XmlEditor xmlElementTreeView.InsertElementAfter(element); } - /// - /// Removes the selected element. - /// - public void RemoveElement() - { - editor.RemoveElement(); - } - /// /// Removes the specified element from the tree. /// @@ -427,14 +441,6 @@ namespace ICSharpCode.XmlEditor xmlElementTreeView.InsertTextNodeAfter(textNode); } - /// - /// Removes the currently selected text node. - /// - public void RemoveTextNode() - { - editor.RemoveTextNode(); - } - /// /// Removes the currently selected text node. /// @@ -477,14 +483,6 @@ namespace ICSharpCode.XmlEditor editor.AppendChildComment(); } - /// - /// Removes the selected comment node from the tree. - /// - public void RemoveComment() - { - editor.RemoveComment(); - } - /// /// Removes the specified xml comment from the tree. /// @@ -525,6 +523,113 @@ namespace ICSharpCode.XmlEditor 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. /// @@ -559,6 +664,14 @@ namespace ICSharpCode.XmlEditor return new AddAttributeDialog(attributeNames); } + /// + /// Deletes the selected node. + /// + protected void XmlElementTreeViewDeleteKeyPressed(object source, EventArgs e) + { + Delete(); + } + #region Forms Designer generated code /// @@ -611,6 +724,7 @@ namespace ICSharpCode.XmlEditor 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; @@ -622,6 +736,7 @@ namespace ICSharpCode.XmlEditor 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 // @@ -714,12 +829,22 @@ namespace ICSharpCode.XmlEditor 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; } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewControl.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewControl.cs index 496700302a..671df936b3 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewControl.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeViewControl.cs @@ -31,6 +31,11 @@ namespace ICSharpCode.XmlEditor After = 1 } + /// + /// Raised when the delete key is pressed. + /// + public event EventHandler DeleteKeyPressed; + public XmlTreeViewControl() { } @@ -279,6 +284,24 @@ namespace ICSharpCode.XmlEditor 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 @@ -292,6 +315,17 @@ namespace ICSharpCode.XmlEditor 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. @@ -494,5 +528,51 @@ namespace ICSharpCode.XmlEditor 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/DisplayBindings/XmlEditor/Project/Src/XmlView.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs index ad39a6d416..945cf1dc4d 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs @@ -23,6 +23,7 @@ using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.TextEditor; +using ICSharpCode.TextEditor.Actions; using ICSharpCode.TextEditor.Document; namespace ICSharpCode.XmlEditor @@ -42,23 +43,39 @@ namespace ICSharpCode.XmlEditor /// public static readonly string CategoryName = "XML"; - XmlEditorControl xmlEditor = new XmlEditorControl(); + /// + /// Edit actions addin tree path for the xml editor control. + /// + static readonly string editActionsPath = "/AddIns/XmlEditor/EditActions"; + + /// + /// Right click menu addin tree path for the xml editor control. + /// + static readonly string contextMenuPath = "/SharpDevelop/ViewContent/XmlEditor/ContextMenu"; + + XmlEditorControl xmlEditor; TextEditorDisplayBindingWrapper.FileChangeWatcher watcher; static MessageViewCategory category; string stylesheetFileName; XmlTreeView xmlTreeView; - + + /// + /// Creates an XmlView that is used by SharpDevelop to provide an + /// XML editor. + /// public XmlView() + : this(new SharpDevelopTextEditorProperties(), XmlSchemaManager.SchemaCompletionDataItems) { - xmlEditor.Dock = DockStyle.Fill; + watcher = new TextEditorDisplayBindingWrapper.FileChangeWatcher(this); - xmlEditor.SchemaCompletionDataItems = XmlSchemaManager.SchemaCompletionDataItems; - xmlEditor.Document.DocumentChanged += DocumentChanged; - + xmlEditor.AddEditActions(GetEditActions()); + xmlEditor.TextAreaContextMenuStrip = MenuService.CreateContextMenu(xmlEditor, contextMenuPath); + + // Add event handlers so we can update the status bar when + // the cursor position changes. xmlEditor.ActiveTextAreaControl.Caret.CaretModeChanged += CaretModeChanged; xmlEditor.ActiveTextAreaControl.Caret.PositionChanged += CaretChanged; xmlEditor.ActiveTextAreaControl.Enter += CaretUpdate; - watcher = new TextEditorDisplayBindingWrapper.FileChangeWatcher(this); // Listen for changes to the xml editor properties. XmlEditorAddInOptions.PropertyChanged += PropertyChanged; @@ -68,6 +85,21 @@ namespace ICSharpCode.XmlEditor xmlTreeView = new XmlTreeView(this); SecondaryViewContents.Add(xmlTreeView); } + + /// + /// Creates an XmlView that is independent of SharpDevelop. This + /// constructor does rely on SharpDevelop being available and is + /// only used for testing the XmlView. + /// + public XmlView(ITextEditorProperties textEditorProperties, XmlSchemaCompletionDataCollection schemas) + { + xmlEditor = new XmlEditorControl(); + xmlEditor.Dock = DockStyle.Fill; + + xmlEditor.TextEditorProperties = textEditorProperties; + xmlEditor.SchemaCompletionDataItems = schemas; + xmlEditor.Document.DocumentChanged += DocumentChanged; + } /// /// Gets the active XmlView. @@ -296,12 +328,12 @@ namespace ICSharpCode.XmlEditor public override void Dispose() { base.Dispose(); - watcher.Dispose(); - - XmlEditorAddInOptions.PropertyChanged -= PropertyChanged; - XmlSchemaManager.UserSchemaAdded -= new EventHandler(UserSchemaAdded); - XmlSchemaManager.UserSchemaRemoved -= new EventHandler(UserSchemaRemoved); - + if (watcher != null) { + watcher.Dispose(); + XmlEditorAddInOptions.PropertyChanged -= PropertyChanged; + XmlSchemaManager.UserSchemaAdded -= new EventHandler(UserSchemaAdded); + XmlSchemaManager.UserSchemaRemoved -= new EventHandler(UserSchemaRemoved); + } xmlEditor.Dispose(); } @@ -1242,5 +1274,13 @@ namespace ICSharpCode.XmlEditor } return schemas.ToArray(); } + + /// + /// Gets the edit actions for the xml editor from the addin tree. + /// + IEditAction[] GetEditActions() + { + return (IEditAction[])(AddInTree.BuildItems(editActionsPath, this, false).ToArray(typeof(IEditAction))); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin index 8c87c4bd71..e9b237d6ce 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.addin @@ -198,6 +198,26 @@ + + + + + @@ -263,26 +283,5 @@ label = "${res:ICSharpCode.XmlEditor.XmlTreeView.AddChildCommentMenuLabel}" class = "ICSharpCode.XmlEditor.AddChildCommentCommand"/> - - - - - - - - - - - - diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj index 670789b637..02acf44313 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj @@ -62,9 +62,6 @@ - - - diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/DeleteTreeNodeWithDeleteKeyTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/DeleteTreeNodeWithDeleteKeyTestFixture.cs new file mode 100644 index 0000000000..d35f23bb9f --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/DeleteTreeNodeWithDeleteKeyTestFixture.cs @@ -0,0 +1,39 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Forms; +using ICSharpCode.XmlEditor; +using NUnit.Framework; +using XmlEditor.Tests.Utils; + +namespace XmlEditor.Tests.Tree +{ + /// + /// Tests that the delete key event is fired so we can + /// + [TestFixture] + public class DeleteTreeNodeWithDeleteKeyTestFixture + { + bool deleteKeyPressEventFired; + + [Test] + public void DeleteKeyPressed() + { + using (DerivedXmlTreeViewControl treeView = new DerivedXmlTreeViewControl()) { + treeView.DeleteKeyPressed += TreeViewDeleteKeyPressed; + treeView.CallProcessCmdKey(Keys.Delete); + } + Assert.IsTrue(deleteKeyPressEventFired); + } + + void TreeViewDeleteKeyPressed(object sender, EventArgs e) + { + deleteKeyPressEventFired = true; + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesInTreeControlTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesInTreeControlTestFixture.cs index 33bbbe831f..ceba74ed12 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesInTreeControlTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesInTreeControlTestFixture.cs @@ -277,5 +277,16 @@ namespace XmlEditor.Tests.Tree Assert.AreEqual("new", node.Text); Assert.AreEqual(element, node.XmlElement); } + + /// + /// Tests that when a comment node is selected the tree view container + /// returns this from the SelectedNode property. + /// + [Test] + public void SelectedNodeWhenCommentNodeSelected() + { + treeView.SelectedNode = childCommentTreeNode; + Assert.AreEqual(childCommentTreeNode.XmlComment, treeViewContainer.SelectedNode); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesTestFixture.cs index 59eb7871fa..04b50c3edb 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/EditCommentNodesTestFixture.cs @@ -113,7 +113,8 @@ namespace XmlEditor.Tests.Tree public void RemoveComment() { mockXmlTreeView.SelectedComment = rootComment; - editor.RemoveComment(); + mockXmlTreeView.SelectedNode = rootComment; + editor.Delete(); Assert.AreEqual(1, mockXmlTreeView.CommentNodesRemoved.Count); Assert.AreSame(rootComment, mockXmlTreeView.CommentNodesRemoved[0]); @@ -126,7 +127,8 @@ namespace XmlEditor.Tests.Tree public void RemoveCommentWhenNoNodeSelected() { mockXmlTreeView.SelectedComment = null; - editor.RemoveComment(); + mockXmlTreeView.SelectedNode = null; + editor.Delete(); Assert.IsFalse(mockXmlTreeView.IsDirty); } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/MenuCommandsTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/MenuCommandsTestFixture.cs index 19b50da102..b0c5aef12d 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/MenuCommandsTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/MenuCommandsTestFixture.cs @@ -350,47 +350,12 @@ namespace XmlEditor.Tests.Tree Assert.AreEqual(String.Empty, insertedTextNode.Value); } - /// - /// Expect nothing to happen since the ICommand.Owner is not - /// set. - /// - [Test] - public void RemoveElementCommandWithNullOwner() - { - RemoveElementCommand command = new RemoveElementCommand(); - command.Run(); - } - - [Test] - public void RemoveRootElementUsingCommand() - { - treeView.SelectedNode = treeView.Nodes[0]; - - RemoveElementCommand command = new RemoveElementCommand(); - command.Owner = treeViewContainer; - command.Run(); - - Assert.AreEqual(0, treeView.Nodes.Count); - Assert.IsTrue(treeViewContainer.IsDirty); - } - - [Test] - public void RemoveTextNodeWithNullOwner() - { - RemoveTextNodeCommand command = new RemoveTextNodeCommand(); - command.Run(); - } - [Test] public void RemoveTextNode() { AddChildTextNode(); - treeView.SelectedNode = bodyTreeNode.Nodes[0]; - - RemoveTextNodeCommand command = new RemoveTextNodeCommand(); - command.Owner = treeViewContainer; - command.Run(); + treeViewContainer.Delete(); Assert.IsFalse(bodyElement.HasChildNodes); Assert.AreEqual(0, bodyTreeNode.Nodes.Count); @@ -419,23 +384,15 @@ namespace XmlEditor.Tests.Tree Assert.IsNotNull(treeNode); } - [Test] - public void RemoveCommentWithNullOwner() - { - RemoveCommentCommand command = new RemoveCommentCommand(); - command.Run(); - } - + /// + /// Removes the selected comment using the delete command. + /// [Test] public void RemoveComment() { AddChildCommentNode(); - treeView.SelectedNode = bodyTreeNode.Nodes[0]; - - RemoveCommentCommand command = new RemoveCommentCommand(); - command.Owner = treeViewContainer; - command.Run(); + treeViewContainer.Delete(); Assert.IsFalse(bodyElement.HasChildNodes); Assert.AreEqual(0, bodyTreeNode.Nodes.Count); diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteInTreeControlTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteInTreeControlTestFixture.cs new file mode 100644 index 0000000000..c1736a8894 --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteInTreeControlTestFixture.cs @@ -0,0 +1,276 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Xml; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.XmlEditor; +using NUnit.Framework; + +namespace XmlEditor.Tests.Tree +{ + /// + /// Tests pasting in the XmlTreeViewContainerControl. + /// + [TestFixture] + public class PasteInTreeControlTestFixture + { + XmlDocument doc; + XmlTreeViewContainerControl treeViewContainerControl; + XmlTreeViewControl treeView; + IClipboardHandler clipboardHandler; + XmlElementTreeNode htmlTreeNode; + XmlElementTreeNode bodyTreeNode; + XmlElementTreeNode paragraphTreeNode; + XmlElement htmlElement; + XmlElement bodyElement; + XmlElement paragraphElement; + XmlTextTreeNode paragraphTextTreeNode; + XmlText paragraphText; + XmlCommentTreeNode bodyCommentTreeNode; + XmlComment bodyComment; + + [SetUp] + public void SetUp() + { + XmlCompletionDataProvider completionDataProvider = new XmlCompletionDataProvider(new XmlSchemaCompletionDataCollection(), null, String.Empty); + treeViewContainerControl = new XmlTreeViewContainerControl(); + treeView = treeViewContainerControl.TreeView; + treeViewContainerControl.LoadXml(GetXml(), completionDataProvider); + doc = treeViewContainerControl.Document; + + clipboardHandler = treeViewContainerControl as IClipboardHandler; + + htmlElement = doc.DocumentElement; + bodyElement = htmlElement.FirstChild as XmlElement; + paragraphElement = bodyElement.SelectSingleNode("p") as XmlElement; + paragraphText = paragraphElement.SelectSingleNode("text()") as XmlText; + bodyComment = bodyElement.SelectSingleNode("comment()") as XmlComment; + + htmlTreeNode = treeView.Nodes[0] as XmlElementTreeNode; + htmlTreeNode.PerformInitialization(); + bodyTreeNode = htmlTreeNode.FirstNode as XmlElementTreeNode; + bodyTreeNode.PerformInitialization(); + bodyCommentTreeNode = bodyTreeNode.FirstNode as XmlCommentTreeNode; + paragraphTreeNode = bodyTreeNode.LastNode as XmlElementTreeNode; + paragraphTreeNode.PerformInitialization(); + paragraphTextTreeNode = paragraphTreeNode.FirstNode as XmlTextTreeNode; + } + + [TearDown] + public void TearDown() + { + if (treeViewContainerControl != null) { + treeViewContainerControl.Dispose(); + } + } + + [Test] + public void ClipboardCopyDisabledWhenNoNodeSelected() + { + treeView.SelectedNode = null; + Assert.IsFalse(clipboardHandler.EnableCopy); + } + + [Test] + public void ClipboardPasteDisabledWhenNoNodeSelected() + { + treeView.SelectedNode = null; + Assert.IsFalse(clipboardHandler.EnablePaste); + } + + /// + /// Select all is not currently implemented since the tree + /// only supports single node selection. + /// + [Test] + public void SelectAllDisabled() + { + Assert.IsFalse(clipboardHandler.EnableSelectAll); + } + + [Test] + public void ClipboardCutDisabledWhenNoNodeSelected() + { + treeView.SelectedNode = null; + Assert.IsFalse(clipboardHandler.EnableCut); + } + + [Test] + public void ClipboardDeleteDisabledWhenNoNodeSelected() + { + treeView.SelectedNode = null; + Assert.IsFalse(clipboardHandler.EnableDelete); + } + + [Test] + public void ClipboardDeleteEnabledWhenNodeSelected() + { + treeView.SelectedNode = treeView.Nodes[0]; + Assert.IsTrue(clipboardHandler.EnableDelete); + } + + /// + /// The cut tree node should have be showing its ghost image + /// after being cut. + /// + [Test] + public void CutBodyElement() + { + treeView.SelectedNode = bodyTreeNode; + treeViewContainerControl.Cut(); + + Assert.AreEqual(XmlElementTreeNode.XmlElementTreeNodeGhostImageKey, bodyTreeNode.ImageKey); + Assert.AreEqual(XmlElementTreeNode.XmlElementTreeNodeGhostImageKey, bodyTreeNode.SelectedImageKey); + Assert.IsTrue(bodyTreeNode.ShowGhostImage); + } + + /// + /// This should append a copy of the root element + /// as a child of the existing root element. All child nodes of the + /// root element should be copied too. + /// + [Test] + public void CopyRootElementAndPasteToRootElement() + { + treeView.SelectedNode = htmlTreeNode; + treeViewContainerControl.Copy(); + bool pasteEnabled = treeViewContainerControl.EnablePaste; + treeViewContainerControl.Paste(); + + XmlElement pastedElement = (XmlElement)htmlElement.LastChild; + XmlElementTreeNode pastedTreeNode = htmlTreeNode.LastNode as XmlElementTreeNode; + Assert.AreEqual(htmlElement.Name, pastedElement.Name); + Assert.AreEqual(pastedElement.Name, pastedTreeNode.Text); + Assert.AreSame(pastedElement, pastedTreeNode.XmlElement); + Assert.IsTrue(pasteEnabled); + } + + [Test] + public void CutAndPasteElement() + { + treeView.SelectedNode = paragraphTreeNode; + treeViewContainerControl.Cut(); + treeView.SelectedNode = htmlTreeNode; + bool pasteEnabled = treeViewContainerControl.EnablePaste; + treeViewContainerControl.Paste(); + + XmlElement pastedElement = htmlElement.LastChild as XmlElement; + XmlElementTreeNode pastedTreeNode = htmlTreeNode.LastNode as XmlElementTreeNode; + + Assert.AreSame(paragraphElement, pastedElement); + Assert.IsNull(bodyElement.SelectSingleNode("p")); + Assert.AreSame(paragraphElement, pastedTreeNode.XmlElement); + Assert.IsTrue(pasteEnabled); + Assert.AreEqual(XmlElementTreeNode.XmlElementTreeNodeImageKey, + pastedTreeNode.ImageKey, + "Should not be ghost image."); + } + + /// + /// Check that the ghost image is removed when the user pastes the + /// cut node back onto itself. + /// + [Test] + public void CutAndPasteElementBackOntoItself() + { + treeView.SelectedNode = paragraphTreeNode; + treeViewContainerControl.Cut(); + treeView.SelectedNode = paragraphTreeNode; + treeViewContainerControl.Paste(); + + Assert.IsFalse(treeViewContainerControl.IsDirty); + Assert.AreSame(XmlElementTreeNode.XmlElementTreeNodeImageKey, paragraphTreeNode.ImageKey); + Assert.AreSame(XmlElementTreeNode.XmlElementTreeNodeImageKey, paragraphTreeNode.SelectedImageKey); + } + + [Test] + public void CopyAndPasteTextNode() + { + treeView.SelectedNode = paragraphTextTreeNode; + treeViewContainerControl.Copy(); + treeView.SelectedNode = bodyTreeNode; + treeViewContainerControl.Paste(); + + XmlText pastedTextNode = bodyElement.LastChild as XmlText; + XmlTextTreeNode pastedTreeNode = bodyTreeNode.LastNode as XmlTextTreeNode; + + Assert.AreEqual(pastedTextNode.InnerText, paragraphText.InnerText); + Assert.AreEqual(paragraphText.InnerText, pastedTreeNode.Text); + Assert.IsTrue(treeViewContainerControl.IsDirty); + } + + [Test] + public void CutAndPasteTextNode() + { + treeView.SelectedNode = paragraphTextTreeNode; + treeViewContainerControl.Cut(); + string paragraphTextTreeNodeImageKeyAfterCut = paragraphTextTreeNode.ImageKey; + treeView.SelectedNode = bodyTreeNode; + treeViewContainerControl.Paste(); + + XmlText pastedTextNode = bodyElement.LastChild as XmlText; + XmlTextTreeNode pastedTreeNode = bodyTreeNode.LastNode as XmlTextTreeNode; + + Assert.AreSame(paragraphText, pastedTextNode); + Assert.IsNull(paragraphElement.SelectSingleNode("text()")); + Assert.AreEqual(XmlTextTreeNode.XmlTextTreeNodeGhostImageKey, + paragraphTextTreeNodeImageKeyAfterCut); + Assert.AreEqual(XmlTextTreeNode.XmlTextTreeNodeImageKey, + pastedTreeNode.ImageKey, + "Should not be ghost image."); + } + + [Test] + public void CopyAndPasteCommentNode() + { + treeView.SelectedNode = bodyCommentTreeNode; + treeViewContainerControl.Copy(); + treeView.SelectedNode = htmlTreeNode; + treeViewContainerControl.Paste(); + + XmlComment pastedCommentNode = htmlElement.LastChild as XmlComment; + XmlCommentTreeNode pastedTreeNode = htmlTreeNode.LastNode as XmlCommentTreeNode; + + Assert.AreEqual(pastedCommentNode.InnerText, bodyComment.InnerText); + Assert.AreEqual(bodyComment.InnerText.Trim(), pastedTreeNode.Text); + Assert.IsTrue(treeViewContainerControl.IsDirty); + } + + [Test] + public void CutAndPasteCommentNode() + { + treeView.SelectedNode = bodyCommentTreeNode; + treeViewContainerControl.Cut(); + string bodyCommentTreeNodeImageKeyAfterCut = bodyCommentTreeNode.ImageKey; + treeView.SelectedNode = htmlTreeNode; + treeViewContainerControl.Paste(); + + XmlComment pastedCommentNode = htmlElement.LastChild as XmlComment; + XmlCommentTreeNode pastedTreeNode = htmlTreeNode.LastNode as XmlCommentTreeNode; + + Assert.AreSame(bodyComment, pastedCommentNode); + Assert.AreSame(bodyComment, pastedTreeNode.XmlComment); + Assert.IsNull(bodyElement.SelectSingleNode("comment()")); + Assert.AreEqual(XmlCommentTreeNode.XmlCommentTreeNodeGhostImageKey, + bodyCommentTreeNodeImageKeyAfterCut); + Assert.AreEqual(XmlCommentTreeNode.XmlCommentTreeNodeImageKey, + pastedTreeNode.ImageKey, + "Should not be ghost image."); + } + + string GetXml() + { + return "\r\n" + + "\t\r\n" + + "\t\t\r\n" + + "\t\t

some text here

\r\n" + + "\t\r\n" + + ""; + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteTestFixture.cs new file mode 100644 index 0000000000..2181e3cd73 --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/PasteTestFixture.cs @@ -0,0 +1,372 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Xml; +using ICSharpCode.XmlEditor; +using NUnit.Framework; +using XmlEditor.Tests.Utils; + +namespace XmlEditor.Tests.Tree +{ + /// + /// Tests pasting nodes with the XmlTreeEditor either from a cut or a copy. + /// Here we are testing the XmlTreeEditor side and now the actual + /// GUI side - XML Tree. + /// + [TestFixture] + public class PasteTestFixture : XmlTreeViewTestFixtureBase + { + XmlElement rootElement; + XmlElement bodyElement; + XmlElement paragraphElement; + XmlComment bodyComment; + XmlText paragraphText; + + [SetUp] + public void Init() + { + base.InitFixture(); + rootElement = editor.Document.DocumentElement; + bodyElement = (XmlElement)rootElement.FirstChild; + paragraphElement = (XmlElement)bodyElement.SelectSingleNode("p"); + bodyComment = (XmlComment)bodyElement.SelectSingleNode("comment()"); + paragraphText = (XmlText)paragraphElement.SelectSingleNode("text()"); + } + + /// + /// Here we take a copy of the root element of the document and paste + /// it on top of itself. This should append a copy of the root element + /// as a child of the existing root element. All child nodes of the + /// root element should be copied too. + /// + [Test] + public void CopyRootElementAndPasteToRootElement() + { + mockXmlTreeView.SelectedElement = rootElement; + editor.Copy(); + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + XmlElement pastedElement = (XmlElement)rootElement.LastChild; + Assert.AreEqual(rootElement.Name, pastedElement.Name); + Assert.AreEqual(rootElement.FirstChild.Name, pastedElement.FirstChild.Name); + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.AreEqual(1, mockXmlTreeView.ChildElementsAdded.Count); + Assert.AreEqual(pastedElement, mockXmlTreeView.ChildElementsAdded[0]); + Assert.IsTrue(pasteEnabled); + } + + /// + /// The selected node is null when the paste is attempted. Here + /// nothing should happen. + /// + [Test] + public void PasteWhenNoNodeSelected() + { + mockXmlTreeView.SelectedNode = rootElement; + editor.Copy(); + mockXmlTreeView.SelectedNode = null; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + Assert.IsFalse(mockXmlTreeView.IsDirty); + Assert.IsFalse(pasteEnabled); + } + + /// + /// Check that the view is informed when a node is cut. This allows the + /// view to give some sort of indication that a particular node is + /// being cut. + /// + [Test] + public void CutElement() + { + mockXmlTreeView.SelectedElement = bodyElement; + editor.Cut(); + + Assert.AreEqual(1, mockXmlTreeView.CutNodes.Count); + Assert.AreEqual(bodyElement, mockXmlTreeView.CutNodes[0]); + Assert.IsFalse(mockXmlTreeView.IsDirty); + } + + [Test] + public void CutAndPasteElement() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Cut(); + mockXmlTreeView.SelectedElement = rootElement; + editor.Paste(); + + XmlElement pastedElement = rootElement.LastChild as XmlElement; + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.AreSame(paragraphElement, pastedElement); + Assert.IsNull(bodyElement.SelectSingleNode("p")); + Assert.AreEqual(1, mockXmlTreeView.ChildElementsAdded.Count); + Assert.AreSame(paragraphElement, mockXmlTreeView.ChildElementsAdded[0]); + Assert.AreEqual(1, mockXmlTreeView.ElementsRemoved.Count); + Assert.AreSame(paragraphElement, mockXmlTreeView.ElementsRemoved[0]); + } + + /// + /// Checks that nothing happens when no node is selected in the tree + /// to cut. + /// + [Test] + public void NoElementSelectedToCut() + { + editor.Cut(); + + Assert.AreEqual(0, mockXmlTreeView.CutNodes.Count); + } + + /// + /// The selected node is null when the copy method is called, but + /// there is a selected node when the paste is attempted. Here + /// nothing should happen. + /// + [Test] + public void CopyWhenNoNodeSelectedThenPaste() + { + mockXmlTreeView.SelectedNode = null; + editor.Copy(); + mockXmlTreeView.SelectedNode = rootElement; + + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + Assert.IsFalse(mockXmlTreeView.IsDirty); + Assert.IsFalse(pasteEnabled); + } + + [Test] + public void IsCopyEnabledWhenNoNodeSelected() + { + Assert.IsFalse(editor.IsCopyEnabled); + } + + [Test] + public void IsCopyEnabledWhenRootElementSelected() + { + mockXmlTreeView.SelectedElement = rootElement; + mockXmlTreeView.SelectedNode = rootElement; + Assert.IsTrue(editor.IsCopyEnabled); + } + + [Test] + public void IsCutEnabledWhenChildElementSelected() + { + mockXmlTreeView.SelectedElement = bodyElement; + mockXmlTreeView.SelectedNode = bodyElement; + Assert.IsTrue(editor.IsCutEnabled); + } + + [Test] + public void IsCutEnabledWhenRootElementSelected() + { + mockXmlTreeView.SelectedElement = rootElement; + mockXmlTreeView.SelectedNode = rootElement; + Assert.IsFalse(editor.IsCutEnabled); + } + + /// + /// The document should not change if the user decides to paste the + /// cut node back to itself. All that should happen is the view + /// updates the cut node so it no longer has the ghost image. + /// + [Test] + public void CutAndPasteToSameNode() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Cut(); + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Paste(); + + Assert.IsFalse(mockXmlTreeView.IsDirty); + Assert.AreEqual(0, mockXmlTreeView.ChildElementsAdded.Count); + Assert.AreEqual(1, mockXmlTreeView.HiddenCutNodes.Count); + Assert.AreSame(paragraphElement, mockXmlTreeView.HiddenCutNodes[0]); + } + + [Test] + public void CutThenPasteTwiceToSameNode() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Cut(); + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Paste(); + mockXmlTreeView.IsDirty = false; + mockXmlTreeView.HiddenCutNodes.Clear(); + mockXmlTreeView.SelectedElement = rootElement; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + Assert.IsFalse(mockXmlTreeView.IsDirty); + Assert.AreEqual(0, mockXmlTreeView.ChildElementsAdded.Count); + Assert.IsFalse(pasteEnabled); + } + + [Test] + public void CannotPasteElementOntoCommentNode() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Copy(); + mockXmlTreeView.SelectedComment = bodyComment; + + Assert.IsFalse(editor.IsPasteEnabled); + } + + [Test] + public void CannotPasteElementOntoTextNode() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Copy(); + mockXmlTreeView.SelectedComment = bodyComment; + + Assert.IsFalse(editor.IsPasteEnabled); + } + + [Test] + public void CopyAndPasteTextNode() + { + mockXmlTreeView.SelectedTextNode = paragraphText; + editor.Copy(); + bool pastedEnabledWhenTextNodeSelected = editor.IsPasteEnabled; + mockXmlTreeView.SelectedElement = bodyElement; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + XmlText pastedTextNode = bodyElement.LastChild as XmlText; + + Assert.IsFalse(pastedEnabledWhenTextNodeSelected); + Assert.IsTrue(pasteEnabled); + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.IsNotNull(pastedTextNode); + Assert.AreEqual(paragraphText.InnerText, pastedTextNode.InnerText); + Assert.AreEqual(1, mockXmlTreeView.ChildTextNodesAdded.Count); + Assert.AreEqual(pastedTextNode, mockXmlTreeView.ChildTextNodesAdded[0]); + } + + [Test] + public void CutAndPasteTextNode() + { + mockXmlTreeView.SelectedTextNode = paragraphText; + editor.Cut(); + mockXmlTreeView.SelectedElement = bodyElement; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + XmlText pastedTextNode = bodyElement.LastChild as XmlText; + + Assert.IsTrue(pasteEnabled); + Assert.IsNotNull(pastedTextNode); + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.IsNull(paragraphElement.SelectSingleNode("text()")); + Assert.AreEqual(1, mockXmlTreeView.ChildTextNodesAdded.Count); + Assert.AreEqual(pastedTextNode, mockXmlTreeView.ChildTextNodesAdded[0]); + Assert.AreEqual(1, mockXmlTreeView.CutNodes.Count); + Assert.AreSame(pastedTextNode, mockXmlTreeView.CutNodes[0]); + Assert.AreEqual(1, mockXmlTreeView.TextNodesRemoved.Count); + Assert.AreSame(paragraphText, mockXmlTreeView.TextNodesRemoved[0]); + } + + [Test] + public void CopyAndPasteCommentNode() + { + mockXmlTreeView.SelectedComment = bodyComment; + editor.Copy(); + bool pastedEnabledWhenCommentSelected = editor.IsPasteEnabled; + mockXmlTreeView.SelectedElement = rootElement; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + XmlComment pastedCommentNode = rootElement.LastChild as XmlComment; + + Assert.IsFalse(pastedEnabledWhenCommentSelected); + Assert.IsTrue(pasteEnabled); + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.IsNotNull(pastedCommentNode); + Assert.AreEqual(bodyComment.InnerText, pastedCommentNode.InnerText); + Assert.AreEqual(1, mockXmlTreeView.ChildCommentNodesAdded.Count); + Assert.AreEqual(pastedCommentNode, mockXmlTreeView.ChildCommentNodesAdded[0]); + } + + [Test] + public void CutAndPasteCommentNode() + { + mockXmlTreeView.SelectedComment = bodyComment; + editor.Cut(); + mockXmlTreeView.SelectedElement = rootElement; + bool pasteEnabled = editor.IsPasteEnabled; + editor.Paste(); + + XmlComment pastedCommentNode = rootElement.LastChild as XmlComment; + + Assert.IsTrue(pasteEnabled); + Assert.IsNotNull(pastedCommentNode); + Assert.IsTrue(mockXmlTreeView.IsDirty); + Assert.IsNull(bodyElement.SelectSingleNode("comment()")); + Assert.AreSame(bodyComment, pastedCommentNode); + Assert.AreEqual(1, mockXmlTreeView.ChildCommentNodesAdded.Count); + Assert.AreEqual(pastedCommentNode, mockXmlTreeView.ChildCommentNodesAdded[0]); + Assert.AreEqual(1, mockXmlTreeView.CutNodes.Count); + Assert.AreSame(pastedCommentNode, mockXmlTreeView.CutNodes[0]); + Assert.AreEqual(1, mockXmlTreeView.CommentNodesRemoved.Count); + Assert.AreSame(pastedCommentNode, mockXmlTreeView.CommentNodesRemoved[0]); + } + + /// + /// Tests that the cut node has its ghost image removed. + /// + [Test] + public void CutThenCopyElement() + { + mockXmlTreeView.SelectedElement = bodyElement; + editor.Cut(); + editor.Copy(); + + Assert.AreEqual(1, mockXmlTreeView.HiddenCutNodes.Count); + Assert.AreSame(bodyElement, mockXmlTreeView.HiddenCutNodes[0]); + } + + /// + /// This test makes sure that the copied node is cleared when + /// the user decides to do a cut. + /// + [Test] + public void CopyThenCutAndPasteElement() + { + mockXmlTreeView.SelectedElement = paragraphElement; + editor.Copy(); + editor.Cut(); + mockXmlTreeView.SelectedElement = rootElement; + editor.Paste(); + + Assert.IsNull(bodyElement.SelectSingleNode("p")); + } + + /// + /// Returns the xhtml strict schema as the default schema. + /// + protected override XmlSchemaCompletionData DefaultSchemaCompletionData { + get { + XmlTextReader reader = ResourceManager.GetXhtmlStrictSchema(); + return new XmlSchemaCompletionData(reader); + } + } + + protected override string GetXml() + { + return "\r\n" + + "\t\r\n" + + "\t\t\r\n" + + "\t\t

some text here

\r\n" + + "\t\r\n" + + ""; + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveElementTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveElementTestFixture.cs index 7ad673816b..aa59b335cf 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveElementTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveElementTestFixture.cs @@ -31,7 +31,7 @@ namespace XmlEditor.Tests.Tree bodyElement = (XmlElement)editor.Document.SelectSingleNode("/html/body"); mockXmlTreeView.SelectedElement = bodyElement; - editor.RemoveElement(); + editor.Delete(); } [Test] @@ -58,7 +58,7 @@ namespace XmlEditor.Tests.Tree { mockXmlTreeView.SelectedElement = null; mockXmlTreeView.IsDirty = false; - editor.RemoveElement(); + editor.Delete(); Assert.IsFalse(mockXmlTreeView.IsDirty); } @@ -68,7 +68,7 @@ namespace XmlEditor.Tests.Tree XmlElement htmlElement = editor.Document.DocumentElement; mockXmlTreeView.SelectedElement = htmlElement; mockXmlTreeView.IsDirty = false; - editor.RemoveElement(); + editor.Delete(); Assert.IsTrue(mockXmlTreeView.IsDirty); Assert.IsNull(editor.Document.DocumentElement); } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveTextNodeTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveTextNodeTestFixture.cs index cffe6bf224..f712e5b625 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveTextNodeTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/RemoveTextNodeTestFixture.cs @@ -31,8 +31,9 @@ namespace XmlEditor.Tests.Tree paragraphElement = (XmlElement)editor.Document.SelectSingleNode("/html/body/p"); XmlText textNode = (XmlText)paragraphElement.FirstChild; mockXmlTreeView.SelectedTextNode = textNode; + mockXmlTreeView.SelectedNode = textNode; - editor.RemoveTextNode(); + editor.Delete(); } [Test] @@ -51,7 +52,8 @@ namespace XmlEditor.Tests.Tree { mockXmlTreeView.IsDirty = false; mockXmlTreeView.SelectedTextNode = null; - editor.RemoveTextNode(); + mockXmlTreeView.SelectedNode = null; + editor.Delete(); Assert.IsFalse(mockXmlTreeView.IsDirty); } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlCommentTreeNodeTests.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlCommentTreeNodeTests.cs index 8bfd5a5b91..656a584dc2 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlCommentTreeNodeTests.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlCommentTreeNodeTests.cs @@ -58,5 +58,21 @@ namespace XmlEditor.Tests.Tree XmlCommentTreeNode node = new XmlCommentTreeNode(comment); Assert.AreSame(comment, node.XmlComment); } + + [Test] + public void GhostImage() + { + XmlComment comment = doc.CreateComment(String.Empty); + XmlCommentTreeNode node = new XmlCommentTreeNode(comment); + + Assert.IsFalse(node.ShowGhostImage); + + node.ShowGhostImage = false; + Assert.AreEqual(XmlCommentTreeNode.XmlCommentTreeNodeImageKey, node.ImageKey); + + node.ShowGhostImage = true; + Assert.IsTrue(node.ShowGhostImage); + Assert.AreEqual(XmlCommentTreeNode.XmlCommentTreeNodeGhostImageKey, node.ImageKey); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTextTreeNodeTextTests.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTextTreeNodeTextTests.cs index 82e22e3f4c..3bf721037d 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTextTreeNodeTextTests.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTextTreeNodeTextTests.cs @@ -63,5 +63,21 @@ namespace XmlEditor.Tests.Tree XmlTextTreeNode node = new XmlTextTreeNode(text); Assert.AreEqual(String.Empty, node.Text); } + + [Test] + public void GhostImage() + { + XmlText text = doc.CreateTextNode("\r\n\r\n\r\n"); + XmlTextTreeNode node = new XmlTextTreeNode(text); + + Assert.IsFalse(node.ShowGhostImage); + + node.ShowGhostImage = false; + Assert.AreEqual(XmlTextTreeNode.XmlTextTreeNodeImageKey, node.ImageKey); + + node.ShowGhostImage = true; + Assert.IsTrue(node.ShowGhostImage); + Assert.AreEqual(XmlTextTreeNode.XmlTextTreeNodeGhostImageKey, node.ImageKey); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewClipboardHandlerTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewClipboardHandlerTestFixture.cs new file mode 100644 index 0000000000..a63bafb1ea --- /dev/null +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewClipboardHandlerTestFixture.cs @@ -0,0 +1,133 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.TextEditor.Document; +using ICSharpCode.XmlEditor; +using NUnit.Framework; +using XmlEditor.Tests.Utils; + +namespace XmlEditor.Tests.Tree +{ + /// + /// Tests the XmlTreeView's IClipboardHandler implementation. Here + /// we are just checking that it calls the XmlTreeViewContainerControl + /// class. + /// + [TestFixture] + public class XmlTreeViewClipboardHandlerTestFixture + { + XmlView xmlView; + XmlTreeView view; + XmlTreeViewContainerControl treeViewContainer; + XmlTreeViewControl treeView; + IClipboardHandler clipboardHandler; + XmlElementTreeNode htmlTreeNode; + XmlElementTreeNode bodyTreeNode; + XmlElementTreeNode paragraphTreeNode; + + [SetUp] + public void SetUp() + { + XmlSchemaCompletionDataCollection schemas = new XmlSchemaCompletionDataCollection(); + xmlView = new XmlView(new DefaultTextEditorProperties(), schemas); + view = new XmlTreeView(xmlView, null, null); + treeViewContainer = (XmlTreeViewContainerControl)view.Control; + treeView = treeViewContainer.TreeView; + clipboardHandler = view as IClipboardHandler; + + xmlView.XmlEditor.Text = "

"; + xmlView.IsDirty = false; + view.Selected(); + + htmlTreeNode = treeView.Nodes[0] as XmlElementTreeNode; + htmlTreeNode.PerformInitialization(); + bodyTreeNode = htmlTreeNode.Nodes[0] as XmlElementTreeNode; + bodyTreeNode.PerformInitialization(); + paragraphTreeNode = bodyTreeNode.Nodes[0] as XmlElementTreeNode; + + treeView.SelectedNode = null; + } + + [TearDown] + public void TearDown() + { + if (view != null) { + view.Dispose(); + } + if (xmlView != null) { + xmlView.Dispose(); + } + } + + [Test] + public void CopyDisabledWhenNoNodeSelected() + { + Assert.IsFalse(clipboardHandler.EnableCopy); + } + + [Test] + public void CutDisabledWhenNoNodeSelected() + { + Assert.IsFalse(clipboardHandler.EnableCut); + } + + [Test] + public void PasteDisabledWhenNoNodeSelected() + { + Assert.IsFalse(clipboardHandler.EnablePaste); + } + + [Test] + public void DeleteDisabledWhenNoNodeSelected() + { + Assert.IsFalse(clipboardHandler.EnableDelete); + } + + [Test] + public void SelectAllDisabled() + { + Assert.IsFalse(clipboardHandler.EnableSelectAll); + } + + [Test] + public void CopyAndPaste() + { + treeView.SelectedNode = htmlTreeNode; + view.Copy(); + view.Paste(); + + Assert.IsTrue(xmlView.IsDirty); + Assert.AreEqual(htmlTreeNode.Text, htmlTreeNode.LastNode.Text); + } + + [Test] + public void CutAndPaste() + { + treeView.SelectedNode = paragraphTreeNode; + view.Cut(); + treeView.SelectedNode = htmlTreeNode; + view.Paste(); + + Assert.IsTrue(xmlView.IsDirty); + Assert.AreEqual(paragraphTreeNode.Text, htmlTreeNode.LastNode.Text); + } + + [Test] + public void DeleteRootElement() + { + treeView.SelectedNode = htmlTreeNode; + + clipboardHandler.Delete(); + + Assert.AreEqual(0, treeView.Nodes.Count); + Assert.IsTrue(xmlView.IsDirty); + } + } +} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewContainerTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewContainerTestFixture.cs index 3e1c3d78a0..df7bbb0b94 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewContainerTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Tree/XmlTreeViewContainerTestFixture.cs @@ -328,13 +328,25 @@ namespace XmlEditor.Tests.Tree [Test] public void XmlElementTreeNodeImageKey() { - Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlTextTreeNode.XmlTextTreeNodeImageKey)); + Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlElementTreeNode.XmlElementTreeNodeGhostImageKey)); + } + + [Test] + public void XmlElementTreeNodeGhostImageKey() + { + Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlElementTreeNode.XmlElementTreeNodeGhostImageKey)); } [Test] public void XmlTextTreeNodeImageKey() { - Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlElementTreeNode.XmlElementTreeNodeImageKey)); + Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlTextTreeNode.XmlTextTreeNodeImageKey)); + } + + [Test] + public void XmlTextTreeNodeGhostImageKey() + { + Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlTextTreeNode.XmlTextTreeNodeGhostImageKey)); } [Test] @@ -343,6 +355,12 @@ namespace XmlEditor.Tests.Tree Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlCommentTreeNode.XmlCommentTreeNodeImageKey)); } + [Test] + public void XmlCommentTreeNodeGhostImageKey() + { + Assert.IsTrue(treeView.ImageList.Images.ContainsKey(XmlCommentTreeNode.XmlCommentTreeNodeGhostImageKey)); + } + /// /// Checks that setting the TextContent property updates /// the text in the text box. @@ -362,6 +380,36 @@ namespace XmlEditor.Tests.Tree Assert.AreSame(doc, treeView.Document); } + /// + /// Tests that when a text node is selected the tree view container + /// returns this from the SelectedNode property. + /// + [Test] + public void SelectedNodeWhenTextNodeSelected() + { + treeView.SelectedNode = textTreeNode; + Assert.AreEqual(textTreeNode.XmlText, treeViewContainer.SelectedNode); + } + + /// + /// Here we make sure the tree view delete key pressed handler + /// removes the selected node. + /// + [Test] + public void DeleteTextNode() + { + treeView.SelectedNode = textTreeNode; + + // Sanity check that the html tree node has a text node child. + Assert.IsNotNull(htmlTreeNode.XmlElement.SelectSingleNode("text()")); + + // Call the delete key handler. This is usually called when + // the delete key is pressed in the xml tree view control. + treeViewContainer.CallXmlElementTreeViewDeleteKeyPressed(); + + Assert.IsNull(htmlTreeNode.XmlElement.SelectSingleNode("text()")); + } + void TreeViewContainerDirtyChanged(object source, EventArgs e) { dirtyChanged = true; diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeView.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeView.cs index 26a5c50f6f..9268bd080c 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeView.cs @@ -25,5 +25,11 @@ namespace XmlEditor.Tests.Utils { base.OnMouseDown(e); } + + public void CallProcessCmdKey(Keys keys) + { + Message msg = new Message(); + base.ProcessCmdKey(ref msg, keys); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeViewContainerControl.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeViewContainerControl.cs index b6c86831ee..d853a00136 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeViewContainerControl.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/DerivedXmlTreeViewContainerControl.cs @@ -100,6 +100,16 @@ namespace XmlEditor.Tests.Utils base.XmlElementTreeViewAfterSelect(this, new TreeViewEventArgs(null, TreeViewAction.ByMouse)); } + /// + /// Allows us to call the XmlTreeViewContainer's + /// XmlElementTreeViewDeleteKeyPressed method to fake the user + /// pressing the delete key in the xml tree view control. + /// + public void CallXmlElementTreeViewDeleteKeyPressed() + { + base.XmlElementTreeViewDeleteKeyPressed(this, new EventArgs()); + } + /// /// Returns a new MockAddElementDialog for testing. /// diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockXmlTreeView.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockXmlTreeView.cs index 740e2f3a05..3fe2f4a172 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockXmlTreeView.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockXmlTreeView.cs @@ -45,7 +45,10 @@ namespace XmlEditor.Tests.Utils List commentNodesRemoved = new List(); List commentNodesInsertedBefore = new List(); List commentNodesInsertedAfter = new List(); - + XmlNode selectedNode; + List cutNodes = new List(); + List hiddenCutNodes = new List(); + public MockXmlTreeView() { } @@ -80,6 +83,7 @@ namespace XmlEditor.Tests.Utils } set { selectedElement = value; + selectedNode = value; } } @@ -89,6 +93,7 @@ namespace XmlEditor.Tests.Utils } set { selectedTextNode = value; + selectedNode = value; } } @@ -98,6 +103,16 @@ namespace XmlEditor.Tests.Utils } set { selectedCommentNode = value; + selectedNode = value; + } + } + + public XmlNode SelectedNode { + get { + return selectedNode; + } + set { + selectedNode = value; } } @@ -213,6 +228,16 @@ namespace XmlEditor.Tests.Utils commentNodesInsertedAfter.Add(comment); } + public void ShowCut(XmlNode node) + { + cutNodes.Add(node); + } + + public void HideCut(XmlNode node) + { + hiddenCutNodes.Add(node); + } + public string TextContent { get { return textContentDisplayed; @@ -453,5 +478,25 @@ namespace XmlEditor.Tests.Utils return commentNodesInsertedAfter; } } + + /// + /// Returns the nodes that used when informing the view + /// that a particular node was about to be cut. + /// + public List CutNodes { + get { + return cutNodes; + } + } + + /// + /// Returns the nodes where the cut has been hidden via + /// the HideCut method. + /// + public List HiddenCutNodes { + get { + return hiddenCutNodes; + } + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj b/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj index d61ef1c807..2cbe0d8c15 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj @@ -72,6 +72,7 @@ + @@ -79,11 +80,14 @@ + + + diff --git a/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln b/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln index 98e3aeda9e..1508b739dc 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln +++ b/src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 9.00 # Visual Studio 2005 -# SharpDevelop 2.1.0.2075 +# SharpDevelop 2.1.0.2147 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor", "Project\XmlEditor.csproj", "{6B717BD1-CD5E-498C-A42E-9E6A4584DC48}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor.Tests", "Test\XmlEditor.Tests.csproj", "{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}" diff --git a/src/Main/Base/Project/Src/Services/IconService.cs b/src/Main/Base/Project/Src/Services/IconService.cs index 60b682f09a..3a83bdf0f7 100644 --- a/src/Main/Base/Project/Src/Services/IconService.cs +++ b/src/Main/Base/Project/Src/Services/IconService.cs @@ -40,7 +40,13 @@ namespace ICSharpCode.SharpDevelop } } + public static Bitmap GetGhostBitmap(string name) + { + return GetGhostBitmap(GetBitmap(name)); + } + + public static Bitmap GetGhostBitmap(Bitmap bitmap) { ColorMatrix clrMatrix = new ColorMatrix(new float[][] { new float[] {1, 0, 0, 0, 0}, @@ -55,7 +61,6 @@ namespace ICSharpCode.SharpDevelop ColorMatrixFlag.Default, ColorAdjustType.Bitmap); - Bitmap bitmap = GetBitmap(name); Bitmap ghostBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); using (Graphics g = Graphics.FromImage(ghostBitmap)) {