/// <summary> /// Inserts the specified text at the current cursor position, if possible /// </summary> private async Task InsertText(XmlCursor cursor, string text, XmlRules xmlRules) { XmlCursorPos insertPos; // If something is selected, then delete it first, because it will be replaced by the new text XmlCursor deleteArea = cursor.Clone(); await deleteArea.OptimizeSelection(); var deleteResult = await XmlCursorSelectionHelper.DeleteSelection(deleteArea); if (deleteResult.Success) { insertPos = deleteResult.NewCursorPosAfterDelete; } else { insertPos = cursor.StartPos.Clone(); } // insert the specified text at the cursor position var replacementNode = InsertAtCursorPosHelper.InsertText(insertPos, text, xmlRules).ReplaceNode; if (replacementNode != null) { // Text could not be inserted because a node input was converted from text input. // Example: In the AIML template, * is pressed, and a <star> is inserted there instead InsertAtCursorPosHelper.InsertXmlNode(insertPos, replacementNode, xmlRules, false); } // then the cursor is only one line behind the inserted text await cursor.SetPositions(insertPos.ActualNode, insertPos.PosOnNode, insertPos.PosInTextNode, throwChangedEventWhenValuesChanged : false); }
/// <summary> /// Finds out if the node or one of its parent nodes is selected /// </summary> public static bool IsThisNodeInsideSelection(XmlCursor cursor, System.Xml.XmlNode node) { // Check if the node itself or one of its parents is directly selected if (cursor.StartPos.IsNodeInsideSelection(node)) { return(true); } if (cursor.EndPos.IsNodeInsideSelection(node)) { return(true); } if (cursor.StartPos.Equals(cursor.EndPos)) // Both positions are the same, so a maximum of one single node is selected { return(cursor.StartPos.IsNodeInsideSelection(node)); } else // Both positions are not equal, so something may be selected { if ((cursor.StartPos.ActualNode == node) || (cursor.EndPos.ActualNode == node)) // Start or EndNode of the selection is this node { if (node is System.Xml.XmlText) // is a text node { return(true); } else // not a text node { return(false); } } else { if (cursor.StartPos.LiesBehindThisPos(node)) // Node is behind the starting position { if (cursor.EndPos.LiesBeforeThisPos(node)) // Node is located between Startpos and Endepos { return(true); } else // Node is behind Startpos but also behind Endpos { return(false); } } else // Node is not behind the start pos { return(false); } } } }
public async Task UnDo() { if (this.UndoHandler == null) { throw new ApplicationException("No Undo-Handler attached, but Undo invoked!"); } else { XmlCursor c = this.UndoHandler.Undo(); if (c != null) // If a CursorPos was stored for this undo step { await this.CursorRaw.SetPositions( c.StartPos.ActualNode, c.StartPos.PosOnNode, c.StartPos.PosInTextNode, c.EndPos.ActualNode, c.EndPos.PosOnNode, c.EndPos.PosInTextNode, throwChangedEventWhenValuesChanged : true); } await this.FireContentChangedEvent(needToSetFocusOnEditorWhenLost : false, forceFullRepaint : false); } }
/// <summary> /// FInserts the specified node at the current cursor position, if possible /// </summary> private async Task XMLNodeEinfuegen(XmlCursor cursor, XmlNode node, XmlRules xmlRules, bool setNewCursorPosBehindNewInsertedNode) { // If something is selected, then delete it first, because it will be replaced by the new text XmlCursor deleteArea = cursor.Clone(); await deleteArea.OptimizeSelection(); var deleteResult = await XmlCursorSelectionHelper.DeleteSelection(deleteArea); if (deleteResult.Success) { await cursor.SetPositions(deleteResult.NewCursorPosAfterDelete.ActualNode, deleteResult.NewCursorPosAfterDelete.PosOnNode, deleteResult.NewCursorPosAfterDelete.PosInTextNode, throwChangedEventWhenValuesChanged : false); } // insert the specified node at the cursor position if (InsertAtCursorPosHelper.InsertXmlNode(cursor.StartPos, node, xmlRules, setNewCursorPosBehindNewInsertedNode)) { // then the cursor is only one line behind the inserted cursor.EndPos.SetPos(cursor.StartPos.ActualNode, cursor.StartPos.PosOnNode, cursor.StartPos.PosInTextNode); } }
/// <summary> /// Delete the node or the character behind the cursor /// </summary> public async Task <bool> ActionDeleteNodeOrSignBehindCursorPos(XmlCursorPos position, SetUndoSnapshotOptions setUnDoSnapshot) { if (!this.ActionsAllowed) { return(false); } if (setUnDoSnapshot == SetUndoSnapshotOptions.Yes) { this.editorState.UndoHandler.SetSnapshot("delete", this.editorState.CursorRaw); } var deleteArea = new XmlCursor(); deleteArea.StartPos.SetPos(position.ActualNode, position.PosOnNode, position.PosInTextNode); var endPos = deleteArea.StartPos.Clone(); await CursorPosMoveHelper.MoveRight(endPos, this.editorState.RootNode, this.xmlRules); deleteArea.EndPos.SetPos(endPos.ActualNode, endPos.PosOnNode, endPos.PosInTextNode); await deleteArea.OptimizeSelection(); if (deleteArea.StartPos.ActualNode == this.editorState.RootNode) { return(false); // You must not delete the rootnot } var deleteResult = await XmlCursorSelectionHelper.DeleteSelection(deleteArea); if (deleteResult.Success) { // After successful deletion the new CursorPos is retrieved here await this.editorState.CursorRaw.SetPositions(deleteResult.NewCursorPosAfterDelete.ActualNode, deleteResult.NewCursorPosAfterDelete.PosOnNode, deleteResult.NewCursorPosAfterDelete.PosInTextNode, throwChangedEventWhenValuesChanged : false); await this.editorState.FireContentChangedEvent(needToSetFocusOnEditorWhenLost : false, forceFullRepaint : false); return(true); } return(false); }
public void SetSnapshot(string snapShotName, XmlCursor cursor) { this.undoSteps[pos].SnapShotName = snapShotName; this.undoSteps[pos].CursorBefore = cursor; }
/// <summary> /// Returns the selected XML content as string /// </summary> public static async Task <string> GetSelectionAsString(XmlCursor cursor) { if (cursor.IsSomethingSelected) { var result = new StringBuilder(); var optimized = cursor.Clone(); await optimized.OptimizeSelection(); System.Xml.XmlNode node = optimized.StartPos.ActualNode; // begin at the start node // Include the start node in the result switch (optimized.StartPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeEndTag: case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorInFrontOfNode: result.Append(node.OuterXml); // take the entire start node break; case XmlCursorPositions.CursorBehindTheNode: case XmlCursorPositions.CursorInsideTheEmptyNode: break; case XmlCursorPositions.CursorInsideTextNode: // take only a part of the text string textPart = node.InnerText; int start = optimized.StartPos.PosInTextNode; int length = textPart.Length - start; if (node == optimized.EndPos.ActualNode) // If this text node is both start and end node { switch (optimized.EndPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeEndTag: case XmlCursorPositions.CursorBehindTheNode: // Length stays to the end of the node break; case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorInsideTheEmptyNode: case XmlCursorPositions.CursorInFrontOfNode: throw new ApplicationException($"XMLCursor.GetSelectionAsString: implausible EndPos.PosOnNode '{optimized.EndPos.PosOnNode}' for StartPos.CursorInsideTextNode"); case XmlCursorPositions.CursorInsideTextNode: // Not quite to the end of the text if (optimized.StartPos.PosInTextNode > optimized.EndPos.PosInTextNode) { throw new ApplicationException("XMLCursor.GetSelectionAsString: optimized.StartPos.PosInTextNode > optimized.EndPos.PosInTextNode"); } else { // Subtract the text from the length after selection length -= (textPart.Length - optimized.EndPos.PosInTextNode); } break; default: throw new ApplicationException($"XMLCursor.GetSelectionAsString: unhandled optimized.EndPos.PosOnNode'{optimized.EndPos.PosOnNode}' for StartPos.CursorInsideTextNode"); } } textPart = textPart.Substring(start, length); result.Append(textPart); break; default: throw new ApplicationException($"XMLCursor.GetSelectionAsStringg: unhandled optimized.StartPos.PosOnNode'{optimized.StartPos.PosOnNode}'"); } if (optimized.StartPos.ActualNode != optimized.EndPos.ActualNode) // If more nodes are selected after the start node { do { node = node.NextSibling; // to the next node... if (node != null) { // Include the node in the result if (node == optimized.EndPos.ActualNode) // This node is the EndNode { switch (optimized.EndPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeEndTag: case XmlCursorPositions.CursorOnNodeStartTag: case XmlCursorPositions.CursorBehindTheNode: result.Append(node.OuterXml); // Include node 1:1 in result break; case XmlCursorPositions.CursorInsideTextNode: // TRake the beginning of the text node string textPart = node.InnerText; result.Append(textPart.Substring(0, optimized.EndPos.PosInTextNode + 1)); break; case XmlCursorPositions.CursorInsideTheEmptyNode: throw new ApplicationException($"XMLCursor.GetSelectionAsString: implausible optimized.EndPos.PosOnNode '{optimized.EndPos.PosOnNode}' for StartPos.Node != EndPos.Node"); default: throw new ApplicationException($"XMLCursor.GetSelectionAsString: implausible optimized.StartPos.PosOnNode'{optimized.StartPos.PosOnNode}' for StartPos.Node != EndPos.Node"); } } else // Include node 1:1 in result { result.Append(node.OuterXml); } } } while ((node != optimized.EndPos.ActualNode) && (node != null)); // ... until the end node is reached if (node == null) { throw new ApplicationException("Endnode was not reachable as NextSibling from Startnode"); } } return(result.ToString()); } else { return(string.Empty); // nothing is selected at all } }
protected abstract Task <PaintContext> PaintInternal(PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, IGraphics gfx, PaintModes paintMode, int depth);
/// <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)); } } }
protected override async Task <PaintContext> PaintInternal(PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, IGraphics gfx, PaintModes paintMode, int depth) { paintContext.PaintPosX += 3; var actualText = ToolboxXml.TextFromNodeCleaned(XmlNode); var selection = this.CalculateStartAndEndOfSelection(actualText, cursor); var actualPaintData = LastPaintingDataText.CalculateActualPaintData(paintContext, cursorBlinkOn, this.XmlNode, actualText, this.Config.FontTextNode.Height, cursor, selection.Start, selection.Length); var alreadyUnpainted = false; switch (paintMode) { case PaintModes.ForcePaintNoUnPaintNeeded: alreadyUnpainted = true; this.lastPaintData = null; break; case PaintModes.ForcePaintAndUnpaintBefore: alreadyUnpainted = true; this.lastPaintData = null; this.UnPaint(gfx); break; case PaintModes.OnlyPaintWhenChanged: if (!actualPaintData.Equals(lastPaintData)) { this.lastPaintData = null; } break; } if (this.lastPaintData != null && this.lastPaintContextResult != null) { return(lastPaintContextResult.Clone()); } this.lastPaintData = actualPaintData; this.cursorPaintPos = null; if (lastFontHeight != this.Config.FontTextNode.Height) { lastFontHeight = this.Config.FontTextNode.Height; lastCalculatedFontWidth = await this.xmlEditor.NativePlatform.Gfx.MeasureDisplayStringWidthAsync("W", this.Config.FontTextNode); } paintContext.HeightActualRow = Math.Max(paintContext.HeightActualRow, this.Config.MinLineHeight); int marginY = (paintContext.HeightActualRow - this.Config.FontTextNode.Height) / 2; const int charMarginRight = 2; var textPartsRaw = TextSplitHelper.SplitText( text: actualText, invertStart: selection.Start, invertLength: selection.Length, maxLength: (int)((paintContext.LimitRight - paintContext.LimitLeft) / lastCalculatedFontWidth) - charMarginRight, maxLengthFirstLine: (int)((paintContext.LimitRight - paintContext.PaintPosX) / lastCalculatedFontWidth) - charMarginRight) .ToArray(); var newTextParts = this.GetTextLinesFromTextParts(textPartsRaw, paintContext, cursorBlinkOn, cursor, lastCalculatedFontWidth).ToArray(); // Now draw the content, if necessary wrap to several text parts and lines for (int i = 0; i < newTextParts.Length; i++) { var newPart = newTextParts[i]; var oldPart = (this.textParts != null && i < this.textParts.Length) ? this.textParts[i] : null; if (alreadyUnpainted == false && newPart.Equals(oldPart)) { // no need to paint the text part again } else { // draw the inverted background if (!alreadyUnpainted && oldPart != null) { gfx.UnPaintRectangle(oldPart.Rectangle); } if (newPart.Inverted || this.colorBackground != this.Config.ColorBackground) { gfx.AddJob(new JobDrawRectangle { Batchable = true, Layer = GfxJob.Layers.TagBackground, Rectangle = newPart.Rectangle, FillColor = newPart.Inverted ? this.colorBackground.InvertedColor : this.colorBackground, }); } // draw the text gfx.AddJob(new JobDrawString { Batchable = false, Layer = GfxJob.Layers.Text, Text = newPart.Text, Color = newPart.Inverted ? this.colorText.InvertedColor : this.colorText, X = newPart.Rectangle.X, Y = newPart.Rectangle.Y + marginY, Font = Config.FontTextNode });; } paintContext.PaintPosY = newPart.Rectangle.Y; paintContext.PaintPosX = newPart.Rectangle.X + newPart.Rectangle.Width; paintContext.FoundMaxX = Math.Max(paintContext.FoundMaxX, paintContext.PaintPosX); } if (this.textParts != null) // unpaint old text parts out of new parts range { for (int i = newTextParts.Length; i < this.textParts.Length; i++) { gfx.UnPaintRectangle(this.textParts[i].Rectangle); } } this.textParts = newTextParts; paintContext.PaintPosX += 2; this.lastPaintContextResult = paintContext.Clone(); return(paintContext.Clone()); }
/// <summary> /// Draws the XML element on the screen /// </summary> public async Task <PaintContext> Paint(PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, IGraphics gfx, PaintModes paintMode, int depth) { if (this.disposed) { return(paintContext); } if (this.XmlNode == null) { return(paintContext); } if (this.xmlEditor == null) { return(paintContext); } paintContext = await PaintInternal(paintContext, cursorBlinkOn, cursor, gfx, paintMode, depth); if (this.cursorPaintPos != null && cursorBlinkOn) { this.PaintCursor(gfx); } return(paintContext); }
private Selection CalculateStartAndEndOfSelection(string actualText, XmlCursor cursor) { var result = new Selection { Start = -1, Length = 0 }; if (cursor.StartPos.ActualNode == this.XmlNode) // The start of the selection is on this node { switch (cursor.StartPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: // The node itself is selected as start node case XmlCursorPositions.CursorOnNodeEndTag: throw new ArgumentOutOfRangeException($"{nameof(cursor.StartPos.PosOnNode)}:{cursor.StartPos.PosOnNode.ToString()} not possible on a text node"); // result.Start = 0; // result.Length = actualText.Length; // break; case XmlCursorPositions.CursorBehindTheNode: case XmlCursorPositions.CursorInsideTheEmptyNode: // Since the cursor position arrives sorted, the EndPos can only lie behind the node result.Start = -1; result.Length = 0; break; case XmlCursorPositions.CursorInFrontOfNode: case XmlCursorPositions.CursorInsideTextNode: if (cursor.StartPos.PosOnNode == XmlCursorPositions.CursorInsideTextNode) { result.Start = Math.Max(0, cursor.StartPos.PosInTextNode); // inside text node } else { result.Start = 0; // in front of the node } if (cursor.EndPos.ActualNode == this.XmlNode) // If the end of the selection also is inside this node { switch (cursor.EndPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: // start node is in front of the node, end node behind: everything is selected case XmlCursorPositions.CursorOnNodeEndTag: case XmlCursorPositions.CursorBehindTheNode: result.Length = Math.Max(0, actualText.Length - result.Start); break; case XmlCursorPositions.CursorInsideTheEmptyNode: result.Start = -1; result.Length = 0; break; case XmlCursorPositions.CursorInsideTextNode: // till the marker in the text result.Length = Math.Max(0, cursor.EndPos.PosInTextNode - result.Start); break; case XmlCursorPositions.CursorInFrontOfNode: result.Length = 0; break; default: throw new ApplicationException("unknown cursor.EndPos.PosOnNode '" + cursor.EndPos.PosOnNode + "'B"); } } else // The end of the selection is not inside this node { if (cursor.EndPos.ActualNode.ParentNode == cursor.StartPos.ActualNode.ParentNode) // If start and end are different, but directly in the same parent { result.Length = Math.Max(0, actualText.Length - result.Start); // Select only the selected part } else // Start and end are different and have different parents { result.Start = 0; result.Length = actualText.Length; // select whole text node } } break; default: throw new ApplicationException("unknown cursor.StartPos.PosOnNode '" + cursor.StartPos.PosOnNode + "'A"); } } else // The start of the selection is not on this node { if (cursor.EndPos.ActualNode == this.XmlNode) // But the end of the selection is { switch (cursor.EndPos.PosOnNode) { case XmlCursorPositions.CursorOnNodeStartTag: // The node itself is selected as End-Node case XmlCursorPositions.CursorOnNodeEndTag: // start node is in front of the node, end node behind: everything is selected case XmlCursorPositions.CursorBehindTheNode: result.Start = 0; result.Length = actualText.Length; break; case XmlCursorPositions.CursorInsideTheEmptyNode: result.Start = -1; result.Length = 0; break; case XmlCursorPositions.CursorInsideTextNode: // Start node is in front of the node, end node in the middle, so select from front to middle if (cursor.EndPos.ActualNode.ParentNode == cursor.StartPos.ActualNode.ParentNode) // If start and end are different, but directly in the same parent { result.Start = 0; result.Length = Math.Max(0, cursor.EndPos.PosInTextNode); // Select only the selected front part } else // Start and end different and different parents { result.Start = 0; result.Length = actualText.Length; // select whole text node } break; case XmlCursorPositions.CursorInFrontOfNode: // Start node is in front of the node, end node also result.Start = -1; result.Length = 0; break; default: throw new ApplicationException("unknown cursor.EndPos.PosOnNode '" + cursor.EndPos.PosOnNode + "'X"); } } else // Neither the start nor the end of the selection lies exactly on this node { if (XmlCursorSelectionHelper.IsThisNodeInsideSelection(EditorState.CursorOptimized, this.XmlNode)) { result.Start = 0; result.Length = actualText.Length; // Select entire text node } } } return(result); }
private IEnumerable <TextPart> GetTextLinesFromTextParts(TextSplitHelper.TextPartRaw[] parts, PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, double fontWidth) { paintContext.HeightActualRow = Math.Max(paintContext.HeightActualRow, this.Config.MinLineHeight); var x = paintContext.PaintPosX; var y = paintContext.PaintPosY; var actualLine = 0; var actualTextPartStartPos = 0; foreach (var part in parts) { var newLine = part.LineNo != actualLine; if (newLine) { actualLine = part.LineNo; y += paintContext.HeightActualRow; x = paintContext.LimitLeft; } var width = (int)(part.Text.Length * fontWidth); var textPart = new TextPart { Text = part.Text, Inverted = part.Inverted, Rectangle = new Rectangle(x, y, width, paintContext.HeightActualRow), CursorPos = -1, CursorBlink = cursorBlinkOn, }; if (this.XmlNode == cursor.StartPos.ActualNode) // is the cursor inside the current text node { // Check if the cursor is within this text part var cursorPos = -1; if (cursor.StartPos.ActualNode == this.XmlNode && !cursor.IsSomethingSelected) { switch (cursor.StartPos.PosOnNode) { case XmlCursorPositions.CursorInFrontOfNode: if (part == parts.First()) { cursorPos = 0; } break; case XmlCursorPositions.CursorBehindTheNode: if (part == parts.Last()) { cursorPos = part.Text.Length; } break; case XmlCursorPositions.CursorInsideTextNode: if ((cursor.StartPos.PosInTextNode >= actualTextPartStartPos) && (cursor.StartPos.PosInTextNode <= actualTextPartStartPos + part.Text.Length)) { cursorPos = (int)(cursor.StartPos.PosInTextNode - actualTextPartStartPos); } break; } } if (cursorPos != -1) { textPart.CursorPos = cursorPos; this.cursorPaintPos = new Point( textPart.Rectangle.X + (int)(textPart.CursorPos * lastCalculatedFontWidth), textPart.Rectangle.Y ); } } x += 1 + width; yield return(textPart); actualTextPartStartPos += part.Text.Length; } }
protected override async Task <PaintContext> PaintInternal(PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, IGraphics gfx, PaintModes paintMode, int depth) { this.nodeDimensions.Update(); var isSelected = cursor?.StartPos != null && cursor?.EndPos != null && XmlCursorSelectionHelper.IsThisNodeInsideSelection(cursor, this.XmlNode); this.CreateChildElementsIfNeeded(gfx); Point newCursorPaintPos = null; bool alreadyUnpainted = false; switch (paintMode) { case PaintModes.ForcePaintNoUnPaintNeeded: alreadyUnpainted = true; break; case PaintModes.ForcePaintAndUnpaintBefore: this.UnPaint(gfx); alreadyUnpainted = true; break; case PaintModes.OnlyPaintWhenChanged: break; } // If the cursor is inside the empty node, then draw the cursor there if (cursor.StartPos.ActualNode == this.XmlNode) { if (cursor.StartPos.PosOnNode == XmlCursorPositions.CursorInFrontOfNode) { // remember position for cursor line newCursorPaintPos = new Point(paintContext.PaintPosX, paintContext.PaintPosY); } } var cursorIsOnThisNode = cursor.StartPos.ActualNode == this.XmlNode || cursor.EndPos.ActualNode == this.XmlNode; paintContext = await this.startTag.Paint(paintContext, cursorIsOnThisNode, cursorBlinkOn, alreadyUnpainted, isSelected, gfx); // If the cursor is inside the empty node, then draw the cursor there if (cursor.StartPos.ActualNode == this.XmlNode) { if (cursor.StartPos.PosOnNode == XmlCursorPositions.CursorInsideTheEmptyNode) { // set position for cursor line newCursorPaintPos = new Point(paintContext.PaintPosX - 1, paintContext.PaintPosY); } } paintContext = await this.PaintSubNodes(paintContext, cursorBlinkOn, cursor, gfx, paintMode, depth); if (this.endTag != null) { paintContext = await this.endTag.Paint(paintContext, cursorIsOnThisNode, cursorBlinkOn, alreadyUnpainted, isSelected, gfx); } // If the cursor is behind the node, then also draw the cursor there if (cursor.StartPos.ActualNode == this.XmlNode) { if (cursor.StartPos.PosOnNode == XmlCursorPositions.CursorBehindTheNode) { newCursorPaintPos = new Point(paintContext.PaintPosX - 1, paintContext.PaintPosY); } } this.cursorPaintPos = newCursorPaintPos; return(paintContext.Clone()); }
protected async Task <PaintContext> PaintSubNodes(PaintContext paintContext, bool cursorBlinkOn, XmlCursor cursor, IGraphics gfx, PaintModes paintMode, int depth) { if (this.XmlNode == null) { throw new ApplicationException("PaintSubNodes:xmlNode is null"); } var childPaintContext = paintContext.Clone(); childPaintContext.LimitLeft = paintContext.LimitLeft + this.Config.ChildIndentX; for (int childLauf = 0; childLauf < this.XmlNode.ChildNodes.Count; childLauf++) { // At this point, the ChildControl object should contain the corresponding instance of the XMLElement control for the current XMLChildNode var childElement = (XmlElement)childElements[childLauf]; var displayType = this.XmlRules.DisplayType(childElement.XmlNode); switch (displayType) { case DisplayTypes.OwnRow: // This child element starts a new row and is then drawn in this row // start new row childPaintContext.LimitLeft = paintContext.LimitLeft + this.Config.ChildIndentX; childPaintContext.PaintPosX = childPaintContext.LimitLeft; childPaintContext.PaintPosY += this.Config.SpaceYBetweenLines + paintContext.HeightActualRow; // line break childPaintContext.HeightActualRow = 0; // no element in this line yet, therefore Height 0 // Set X-cursor to the start of the new line // line down and then right into the ChildElement // Line down bool paintLines = false; if (paintLines) { gfx.AddJob(new JobDrawLine { Layer = GfxJob.Layers.TagBorder, Batchable = true, Color = Color.LightGray, X1 = paintContext.LimitLeft, Y1 = paintContext.PaintPosY + this.Config.MinLineHeight / 2, X2 = paintContext.LimitLeft, Y2 = childPaintContext.PaintPosY + this.Config.MinLineHeight / 2 }); // Line to the right with arrow on ChildElement gfx.AddJob(new JobDrawLine { Layer = GfxJob.Layers.TagBorder, Batchable = true, Color = Color.LightGray, X1 = paintContext.LimitLeft, Y1 = childPaintContext.PaintPosY + this.Config.MinLineHeight / 2, X2 = childPaintContext.LimitLeft, Y2 = childPaintContext.PaintPosY + this.Config.MinLineHeight / 2 }); } childPaintContext = await childElement.Paint(childPaintContext, cursorBlinkOn, cursor, gfx, paintMode, depth + 1); break; case DisplayTypes.FloatingElement: // This child is a floating element; it inserts itself into the same line as the previous element and does not start a new line unless the current line is already too long if (childPaintContext.PaintPosX > paintContext.LimitRight) // If the row is already too long { // to next row paintContext.PaintPosY += paintContext.HeightActualRow + this.Config.SpaceYBetweenLines; paintContext.HeightActualRow = 0; paintContext.PaintPosX = paintContext.RowStartX; } else // fits into this line { // set the child to the right of it } childPaintContext = await childElement.Paint(childPaintContext, cursorBlinkOn, cursor, gfx, paintMode, depth + 1); break; default: throw new ArgumentOutOfRangeException(nameof(displayType) + ":" + displayType.ToString()); } paintContext.FoundMaxX = childPaintContext.FoundMaxX; paintContext.PaintPosX = childPaintContext.PaintPosX; paintContext.PaintPosY = childPaintContext.PaintPosY; } // If we have more ChildControls than XMLChildNodes, then delete them at the end of the ChildControl list while (this.XmlNode.ChildNodes.Count < childElements.Count) { var deleteChildElement = childElements[childElements.Count - 1]; deleteChildElement.UnPaint(gfx); childElements.Remove(childElements[childElements.Count - 1]); deleteChildElement.Dispose(); } return(paintContext); }
public static LastPaintingDataText CalculateActualPaintData(PaintContext paintContext, bool cursorBlinkOn, XmlNode node, string actualText, int fontHeight, XmlCursor cursor, int selectionStart, int selectionLength) { return(new LastPaintingDataText { LastCursor = cursor.Clone(), LastPaintPosY = paintContext.PaintPosY, LastPaintPosX = paintContext.PaintPosX, LastPaintLimitRight = paintContext.LimitRight, LastPaintContent = actualText, LastPaintTextFontHeight = fontHeight, SelectionStart = selectionStart, SelectionLength = selectionLength, CursorInNode = cursor.StartPos.ActualNode == node, CursorPosInNode = cursor.StartPos.PosInTextNode, CursorBlinkOn = cursorBlinkOn, LastCursorPos = cursor.StartPos.PosOnNode, }); }