/// <summary> /// Inserts a text between two nodes /// </summary> internal static void InsertTextBetweenTwoNodes(XmlCursorPos cursorPos, System.Xml.XmlNode nodeBefore, System.Xml.XmlNode nodeAfter, string text, XmlRules xmlRules) { if (ToolboxXml.IsTextOrCommentNode(nodeBefore)) // if the node is already text before, then simply append to it { nodeBefore.InnerText += text; cursorPos.SetPos(nodeBefore, XmlCursorPositions.CursorInsideTextNode, nodeBefore.InnerText.Length); } else // the node before is no text { if (ToolboxXml.IsTextOrCommentNode(nodeAfter)) // if the node behind it is already text then just paste it into { nodeAfter.InnerText = $"{text}{nodeAfter.InnerText}"; cursorPos.SetPos(nodeAfter, XmlCursorPositions.CursorInsideTextNode, text.Length); } else // the node behind is also no text { // Insert between two non-text nodes if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos)) { System.Xml.XmlText newTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text); InsertXmlNode(cursorPos, newTextNode, xmlRules, false); } else { #warning Insert another correct message or sound Debug.Assert(false, "Error - Beep!"); } } } }
/// <summary> /// Sets the cursor positions for corresponding mouse actions: For MouseDown StartAndEndpos, for Move and Up only the endpos /// </summary> public async Task SetCursorByMouseAction(XmlNode xmlNode, XmlCursorPositions cursorPos, int posInLine, MouseClickActions action) { switch (action) { case MouseClickActions.MouseDown: // move the cursor to the new position await SetPositions(xmlNode, cursorPos, posInLine, throwChangedEventWhenValuesChanged : true); break; case MouseClickActions.MouseDownMove: case MouseClickActions.MouseUp: // Set end of the select cursor if (EndPos.SetPos(xmlNode, cursorPos, posInLine)) { await this.ForceChangedEvent(); } break; } }
private bool CheckNodePos(System.Xml.XmlNode node) { // Comment is always ok //if (node is System.Xml.XmlComment) return true; // Whitespace is always ok if (node is System.Xml.XmlWhitespace) { return(true); } if (dtd.IsDtdElementKnown(Dtd.GetElementNameFromNode(node))) // The element of this node is known in the DTD { try { if (this.NodeChecker.IsTheNodeAllowedAtThisPos(node)) { return(true); } else { errorMessages.AppendFormat($"Tag '{node.Name}' not allowed here."); var pos = new XmlCursorPos(); pos.SetPos(node, XmlCursorPositions.CursorOnNodeStartTag); var allowedTags = this.NodeChecker.AtThisPosAllowedTags(pos, false, false); // what is allowed at this position? if (allowedTags.Length > 0) { errorMessages.Append("At this position allowed:"); foreach (string tag in allowedTags) { errorMessages.AppendFormat("{0} ", tag); } } else { errorMessages.Append("No tags are allowed at this point. Probably the parent tag is already invalid."); } return(false); } } catch (Dtd.XMLUnknownElementException e) { errorMessages.AppendFormat($"unknown element '{e.ElementName}'"); return(false); } } else // The element of this node is not known in the DTD { errorMessages.AppendFormat($"unknown element '{Dtd.GetElementNameFromNode(node)}'"); return(false); } }
/// <summary> /// Is the specified element allowed at this point in the XML? /// </summary> public bool IsTheNodeAllowedAtThisPos(System.Xml.XmlNode node) { if (node.ParentNode is System.Xml.XmlDocument) { // It is the root element, this cannot be checked against the parent node, but must be compared separately. If it is the root element allowed in the DTD, then ok, otherwise not // Implementation: TO DO! return(true); } else { var cursorPos = new XmlCursorPos(); cursorPos.SetPos(node, XmlCursorPositions.CursorOnNodeStartTag); #if ThinkLogging _thinkLog = new StringBuilder(); #endif // Create the test patterns to insert for all available elements var elementName = Dtd.GetElementNameFromNode(node); var pattern = this.CreateTestPattern(elementName, cursorPos); // Pack into a test sample list and send the list for testing var list = new List <DtdTestpattern>(); list.Add(pattern); this.CheckAllTestPattern(list, cursorPos); if (pattern.Success) { #if ThinkLogging _thinkLog = new StringBuilder(); _thinkLog.Append(pattern.Summary + "\r\n"); #endif return(true); } else { return(false); } } }
/// <summary> /// Replaces the root node of the editor with the content of the clipboard /// </summary> private async Task <bool> ActionReplaceRootNodeByClipboardContent(SetUndoSnapshotOptions setUnDoSnapshot) { if (!ActionsAllowed) { return(false); } try { var text = await this.nativePlatform.Clipboard.GetText(); using (var reader = new XmlTextReader(text, XmlNodeType.Element, null)) { reader.MoveToContent(); // Move to the cd element node. // Create a new root node from the clipboard, from which we can then steal the children var pasteNode = this.editorState.RootNode.OwnerDocument.ReadNode(reader); if (pasteNode.Name != this.editorState.RootNode.Name) { // The node in the clipboard and the current root node do not have the same name return(false); // not allowed } if (setUnDoSnapshot == SetUndoSnapshotOptions.Yes) { this.editorState.UndoHandler.SetSnapshot("replace root node by clipboard content", this.editorState.CursorRaw); } // Delete all children + attributes of the previous root node this.editorState.RootNode.RemoveAll(); // Copy all attributes of the clipboard root node to the correct root node while (pasteNode.Attributes.Count > 0) { var attrib = pasteNode.Attributes.Remove(pasteNode.Attributes[0]); // Remove from clipboard root node this.editorState.RootNode.Attributes.Append(attrib); // put to the right root node } var startPos = new XmlCursorPos(); startPos.SetPos(this.editorState.RootNode, XmlCursorPositions.CursorInsideTheEmptyNode); XmlCursorPos endPos; // Now insert all children of the virtual root node one after the other at the CursorPos endPos = startPos.Clone(); // Before inserting start- and endPos are equal while (pasteNode.ChildNodes.Count > 0) { var child = pasteNode.RemoveChild(pasteNode.FirstChild); this.editorState.RootNode.AppendChild(child); } await this.editorState.CursorRaw.SetPositions(this.editorState.RootNode, XmlCursorPositions.CursorOnNodeStartTag, 0, throwChangedEventWhenValuesChanged : false); await this.editorState.FireContentChangedEvent(needToSetFocusOnEditorWhenLost : false, forceFullRepaint : false); return(true); } } catch (Exception e) { this.nativePlatform.LogError($"ActionReplaceRootNodeByClipboardContent: error for insert text 'text': {e.Message}"); return(false); } }
internal static async Task <bool> MoveLeft(XmlCursorPos cursorPos, System.Xml.XmlNode rootnode, XmlRules xmlRules) { var actualNode = cursorPos.ActualNode; switch (cursorPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorOnNodeEndTag: cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); break; case XmlCursorPositions.CursorInFrontOfNode: if (actualNode != rootnode) { if (actualNode.PreviousSibling != null) { cursorPos.SetPos(actualNode.PreviousSibling, XmlCursorPositions.CursorBehindTheNode); await MoveLeft(cursorPos, rootnode, xmlRules); } else // no previous sibling node available { cursorPos.SetPos(actualNode.ParentNode, XmlCursorPositions.CursorInFrontOfNode); } } else { return(false); } break; case XmlCursorPositions.CursorBehindTheNode: if (ToolboxXml.IsTextOrCommentNode(actualNode)) // With a text node the cursor is placed behind the last character { cursorPos.SetPos(actualNode, XmlCursorPositions.CursorInsideTextNode, Math.Max(0, ToolboxXml.TextFromNodeCleaned(actualNode).Length - 1)); } else { if (actualNode.ChildNodes.Count == 0) { if (xmlRules.HasEndTag(actualNode)) { // If the cursor shows a close tag, place it in the empty node cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInsideTheEmptyNode); } else { // If the cursor does *not* show a close tag, place it before the empty node cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); } } else // there are children in the node { cursorPos.SetPos(actualNode.LastChild, XmlCursorPositions.CursorBehindTheNode); } } break; case XmlCursorPositions.CursorInsideTheEmptyNode: cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); break; case XmlCursorPositions.CursorInsideTextNode: if (ToolboxXml.IsTextOrCommentNode(actualNode)) // Node is text node { if (cursorPos.PosInTextNode > 1) { // Cursor one character to the left cursorPos.SetPos(cursorPos.ActualNode, cursorPos.PosOnNode, cursorPos.PosInTextNode - 1); } else { // Put in front of the node cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); } } else // not a text node { throw new ApplicationException(string.Format("XMLCursorPos.MoveLeft: CursorPos is XMLCursorPositions.CursorInsideTextNodes, but no text node is selected, but the node {0}", actualNode.OuterXml)); } break; default: throw new ApplicationException(String.Format("XMLCursorPos.MoveLeft: unknown CursorPos {0}", cursorPos.PosOnNode)); } return(true); }
internal static async Task <bool> MoveRight(XmlCursorPos cursorPos, System.Xml.XmlNode rootnode, XmlRules xmlRules) { System.Xml.XmlNode node = cursorPos.ActualNode; switch (cursorPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorOnNodeEndTag: cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); break; case XmlCursorPositions.CursorBehindTheNode: if (node.NextSibling != null) { // Place in front of the next sibling cursorPos.SetPos(node.NextSibling, XmlCursorPositions.CursorInFrontOfNode); // Since "behind the first" looks the same as "before the second", move one more step to the right await MoveRight(cursorPos, rootnode, xmlRules); } else // No following siblings available, then set behind the parent node { if (node.ParentNode != rootnode) { cursorPos.SetPos(node.ParentNode, XmlCursorPositions.CursorBehindTheNode); if (!xmlRules.HasEndTag(node.ParentNode)) { // If no closed tag is displayed for the parent, then one more to the right await MoveRight(cursorPos, rootnode, xmlRules); } } else { return(false); } } break; case XmlCursorPositions.CursorInsideTheEmptyNode: cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); break; case XmlCursorPositions.CursorInFrontOfNode: if (ToolboxXml.IsTextOrCommentNode(node)) // The node itself is text node { if (ToolboxXml.TextFromNodeCleaned(node).Length > 1) // Text node is not empty { cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, 1); // one character forward, i.e. after the first character } else // Text node is empty { cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); } } else // Node is not a text node { if (node.ChildNodes.Count == 0) { if (!xmlRules.HasEndTag(node)) // If no closed tag is displayed for this node, then directly behind the node { cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); } else // Node has closing tag, so put it in between { // Set to the empty node cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorInsideTheEmptyNode); } } else // Children available { cursorPos.SetPos(node.FirstChild, XmlCursorPositions.CursorInFrontOfNode); } } break; case XmlCursorPositions.CursorInsideTextNode: if (ToolboxXml.IsTextOrCommentNode(node)) // Node is text node { if (ToolboxXml.TextFromNodeCleaned(node).Length > cursorPos.PosInTextNode + 1) // there is text in the text node on the right { // one character forward, i.e. after the first character cursorPos.SetPos(cursorPos.ActualNode, cursorPos.PosOnNode, cursorPos.PosInTextNode + 1); /*if ((XMLEditor.TextAusTextNodeBereinigt(node).Length == cursor.PosInNode) && (node.NextSibling != null)) * { * // If after the last drawing of the text node and following sibling exists, then before the following sibling node * }*/ } else // no text follows in the text node { cursorPos.SetPos(cursorPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); } } else // Node is not a text node { throw new ApplicationException(String.Format("XMLCurorPos.MoveRight: CursorPos is XMLCursorPositions.CursorInsideTextNodes, but no text node is selected, but the node {0}", node.OuterXml)); } break; default: throw new ApplicationException(String.Format("XMLCurorPos.MoveRight: unknown CursorPos {0}", cursorPos.PosOnNode)); } return(true); }
/// <summary> /// Deletes the characters and nodes between StartPos and EndPos of the cursor /// </summary> internal static async Task <DeleteSelectionResult> DeleteSelection(XmlCursor cursor) { // If the cursor contains no selection at all if (!cursor.IsSomethingSelected) { return(new DeleteSelectionResult { NewCursorPosAfterDelete = cursor.StartPos.Clone(), // Cursor is not changed Success = false // nothing deleted }); } else { if (cursor.StartPos.ActualNode == cursor.EndPos.ActualNode) // If both nodes are identical { switch (cursor.StartPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorOnNodeEndTag: // a single node is selected and should be deleted System.Xml.XmlNode nodeDelete = cursor.StartPos.ActualNode; // This node should be deleted System.Xml.XmlNode nodeBefore = nodeDelete.PreviousSibling; // This node is before the node to be deleted System.Xml.XmlNode nodeAfter = nodeDelete.NextSibling; // This node lies behind the node to be deleted var newPosAfterDelete = new XmlCursorPos(); // This neighboring node will get the cursor after deletion // If the node to be deleted is located between two text nodes, then these two text nodes are combined into one if (nodeBefore != null && nodeAfter != null) { if (nodeBefore is System.Xml.XmlText && nodeAfter is System.Xml.XmlText) { // the node to be deleted lies between two text nodes, therefore these two text nodes are combined into one // Afterwards, the cursor is positioned at the insertion point between the two text modules newPosAfterDelete.SetPos(nodeBefore, XmlCursorPositions.CursorInsideTextNode, nodeBefore.InnerText.Length); nodeBefore.InnerText += nodeAfter.InnerText; // Append the text from after node to the before node // Delete node to be deleted nodeDelete.ParentNode.RemoveChild(nodeDelete); // delete after node nodeAfter.ParentNode.RemoveChild(nodeAfter); return(new DeleteSelectionResult { NewCursorPosAfterDelete = newPosAfterDelete, Success = true }); } } // The node to be deleted is *not* between two text nodes // Determine what should be selected after deletion if (nodeBefore != null) { // After deletion, the cursor is positioned behind the previous node newPosAfterDelete.SetPos(nodeBefore, XmlCursorPositions.CursorBehindTheNode); } else { if (nodeAfter != null) { // After deletion, the cursor is positioned before the following node newPosAfterDelete.SetPos(nodeAfter, XmlCursorPositions.CursorInFrontOfNode); } else { // After deletion, the cursor is in the parent node newPosAfterDelete.SetPos(nodeDelete.ParentNode, XmlCursorPositions.CursorInsideTheEmptyNode); } } // delete the node nodeDelete.ParentNode.RemoveChild(nodeDelete); return(new DeleteSelectionResult { NewCursorPosAfterDelete = newPosAfterDelete, Success = true }); case XmlCursorPositions.CursorInFrontOfNode: // Start and end of the deletion area point to the same node and the start is before the node: This only makes sense with a text node! if (ToolboxXml.IsTextOrCommentNode(cursor.StartPos.ActualNode)) { // Place the cursor in the text node before the first character and then resend cursor.StartPos.SetPos(cursor.StartPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, 0); return(await DeleteSelection(cursor)); // resend to delete } else { // if it is not a text node, then select the whole node and send it again await cursor.SetBothPositionsAndFireChangedEventIfChanged(cursor.StartPos.ActualNode, XmlCursorPositions.CursorOnNodeStartTag); return(await DeleteSelection(cursor)); // resend to delete } case XmlCursorPositions.CursorBehindTheNode: // Start and end of the deletion area point to the same node and the start is behind the node if (ToolboxXml.IsTextOrCommentNode(cursor.StartPos.ActualNode)) { // Place the cursor in the text node before the first character and then resend cursor.StartPos.SetPos(cursor.StartPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, cursor.StartPos.ActualNode.InnerText.Length); return(await DeleteSelection(cursor)); // resend to delete } else { // if it is not a text node, then select the whole node and send it again await cursor.SetBothPositionsAndFireChangedEventIfChanged(cursor.StartPos.ActualNode, XmlCursorPositions.CursorOnNodeStartTag); return(await DeleteSelection(cursor)); // resend to delete } case XmlCursorPositions.CursorInsideTextNode: // a part of a text node is to be deleted // Determine the part of the text to be deleted int startpos = cursor.StartPos.PosInTextNode; int endpos = cursor.EndPos.PosInTextNode; if (cursor.EndPos.PosOnNode == XmlCursorPositions.CursorBehindTheNode) { // If the end of the selection is behind the text node, then all remaining text is selected endpos = cursor.StartPos.ActualNode.InnerText.Length; } // If all text is selected, then delete the entire text node if (startpos == 0 && endpos >= cursor.StartPos.ActualNode.InnerText.Length) { // The whole text node is to be deleted, this is passed on to the method for deleting individually selected nodes XmlCursor nodeSelectedCursor = new XmlCursor(); await nodeSelectedCursor.SetBothPositionsAndFireChangedEventIfChanged(cursor.StartPos.ActualNode, XmlCursorPositions.CursorOnNodeStartTag); return(await DeleteSelection(nodeSelectedCursor)); } else { // Only a part of the text is to be deleted string restText = cursor.StartPos.ActualNode.InnerText; restText = restText.Remove(startpos, endpos - startpos); cursor.StartPos.ActualNode.InnerText = restText; // determine where the cursor is after deletion newPosAfterDelete = new XmlCursorPos(); if (startpos == 0) // The cursor is positioned before the first character { // then it can better be placed before the text node itself newPosAfterDelete.SetPos(cursor.StartPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); } else { newPosAfterDelete.SetPos(cursor.StartPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, startpos); } return(new DeleteSelectionResult { NewCursorPosAfterDelete = newPosAfterDelete, Success = true }); } case XmlCursorPositions.CursorInsideTheEmptyNode: if (cursor.EndPos.PosOnNode == XmlCursorPositions.CursorBehindTheNode || cursor.EndPos.PosOnNode == XmlCursorPositions.CursorInFrontOfNode) { XmlCursor newCursor = new XmlCursor(); await newCursor.SetBothPositionsAndFireChangedEventIfChanged(cursor.StartPos.ActualNode, XmlCursorPositions.CursorOnNodeStartTag, 0); return(await DeleteSelection(newCursor)); } else { throw new ApplicationException($"DeleteSelection:#6363S undefined Endpos '{cursor.EndPos.PosOnNode}'!"); } default: // what else should be selected besides text and the node itself, if start node and end node are identical? throw new ApplicationException($"DeleteSelection:#63346 StartPos.PosAmNode '{cursor.StartPos.PosOnNode}' not allowed!"); } } else // Both nodes are not identical { // If both nodes are not identical, then remove all nodes in between until the two nodes are behind each other while (cursor.StartPos.ActualNode.NextSibling != cursor.EndPos.ActualNode) { cursor.StartPos.ActualNode.ParentNode.RemoveChild(cursor.StartPos.ActualNode.NextSibling); } // delete the endnode or a part of it XmlCursor temp = cursor.Clone(); temp.StartPos.SetPos(cursor.EndPos.ActualNode, XmlCursorPositions.CursorInFrontOfNode); await DeleteSelection(temp); // delete the start node, or a part of it // -> Done by recursion in the selection delete method cursor.EndPos.SetPos(cursor.StartPos.ActualNode, XmlCursorPositions.CursorBehindTheNode); return(await DeleteSelection(cursor)); } } }
/// <summary> /// Inserts the specified XML node at the specified position /// </summary> internal static bool InsertXmlNode(XmlCursorPos cursorPos, System.Xml.XmlNode node, XmlRules xmlRules, bool setNewCursorPosBehindNewInsertedNode) { System.Xml.XmlNode parentNode = cursorPos.ActualNode.ParentNode; switch (cursorPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: // replace the acual node case XmlCursorPositions.CursorOnNodeEndTag: parentNode.ReplaceChild(node, cursorPos.ActualNode); break; case XmlCursorPositions.CursorInFrontOfNode: // insert before actual node parentNode.InsertBefore(node, cursorPos.ActualNode); break; case XmlCursorPositions.CursorBehindTheNode: // insert after actual node parentNode.InsertAfter(node, cursorPos.ActualNode); break; case XmlCursorPositions.CursorInsideTheEmptyNode: // insert into empty node cursorPos.ActualNode.AppendChild(node); break; case XmlCursorPositions.CursorInsideTextNode: // insert into textnode // Make the text available as a node before the insertion position string textDavor = cursorPos.ActualNode.InnerText.Substring(0, cursorPos.PosInTextNode); System.Xml.XmlNode textDavorNode = parentNode.OwnerDocument.CreateTextNode(textDavor); // Provide the text behind the insert position as a node string textAfter = cursorPos.ActualNode.InnerText.Substring(cursorPos.PosInTextNode, cursorPos.ActualNode.InnerText.Length - cursorPos.PosInTextNode); System.Xml.XmlNode textAfterNode = parentNode.OwnerDocument.CreateTextNode(textAfter); // Insert the node to be inserted between the new before and after text node // -> so replace the old text node with // textbefore - newNode - textafter parentNode.ReplaceChild(textDavorNode, cursorPos.ActualNode); parentNode.InsertAfter(node, textDavorNode); parentNode.InsertAfter(textAfterNode, node); break; default: throw new ApplicationException(String.Format("InsertXmlNode: unknown PosOnNode {0}", cursorPos.PosOnNode)); } // set cursor if (setNewCursorPosBehindNewInsertedNode) { // Place cursor behind the new node cursorPos.SetPos(node, XmlCursorPositions.CursorBehindTheNode); } else { if (xmlRules.HasEndTag(node)) { // Place cursor in the new node cursorPos.SetPos(node, XmlCursorPositions.CursorInsideTheEmptyNode); } else { // Place cursor behind the new node cursorPos.SetPos(node, XmlCursorPositions.CursorBehindTheNode); } } return(true); }
/// <summary> /// Inserts the specified text at the current cursor position, if possible /// </summary> internal static InsertTextResult InsertText(XmlCursorPos cursorPos, string rawText, XmlRules xmlRules) { // Revise the entered text in preprocessing if necessary. // In an AIML 1.1 DTD this can mean, for example, that the text for insertion into the PATTERN tag is changed to upper case string text = xmlRules.InsertTextTextPreProcessing(rawText, cursorPos, out System.Xml.XmlNode replacementNode); if (replacementNode != null) { // If the entered text has become a node instead, e.g. with AIML, // if you press * in a template and then want to insert a <star> instead return(new InsertTextResult { ReplaceNode = replacementNode }); } switch (cursorPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorOnNodeEndTag: // First check whether this node may be replaced by a text node if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos)) { // The selected node by a newly created text node System.Xml.XmlText neuerTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text); cursorPos.ActualNode.ParentNode.ReplaceChild(cursorPos.ActualNode, neuerTextNode); cursorPos.SetPos(neuerTextNode, XmlCursorPositions.CursorBehindTheNode); } throw new ApplicationException(String.Format("InsertText: unknown CursorPos {0}", cursorPos.PosOnNode)); case XmlCursorPositions.CursorBehindTheNode: InsertTextBetweenTwoNodes(cursorPos, cursorPos.ActualNode, cursorPos.ActualNode.NextSibling, text, xmlRules); break; case XmlCursorPositions.CursorInFrontOfNode: InsertTextBetweenTwoNodes(cursorPos, cursorPos.ActualNode.PreviousSibling, cursorPos.ActualNode, text, xmlRules); break; case XmlCursorPositions.CursorInsideTheEmptyNode: // First check if text is allowed inside the empty node if (xmlRules.IsThisTagAllowedAtThisPos("#PCDATA", cursorPos)) { // Then create a text node within the empty node with the desired text content System.Xml.XmlText newTextNode = cursorPos.ActualNode.OwnerDocument.CreateTextNode(text); cursorPos.ActualNode.AppendChild(newTextNode); cursorPos.SetPos(newTextNode, XmlCursorPositions.CursorBehindTheNode); } else { // Error BEEEEP! } break; case XmlCursorPositions.CursorInsideTextNode: string textBeforeNode = cursorPos.ActualNode.InnerText.Substring(0, cursorPos.PosInTextNode); string textAfterCursor = cursorPos.ActualNode.InnerText.Substring(cursorPos.PosInTextNode, cursorPos.ActualNode.InnerText.Length - cursorPos.PosInTextNode); // Insert the character of the pressed keys after the cursor cursorPos.ActualNode.InnerText = $"{textBeforeNode}{text}{textAfterCursor}"; cursorPos.SetPos(cursorPos.ActualNode, cursorPos.PosOnNode, cursorPos.PosInTextNode + text.Length); break; default: throw new ApplicationException(String.Format("InsertText: unknown CursorPos {0}", cursorPos.PosOnNode)); } return(new InsertTextResult { ReplaceNode = replacementNode }); }