/// <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>
        /// deletes the actual cursor selection
        /// </summary>
        /// <returns>true, if deleted successfully</returns>
        public virtual async Task <bool> ActionDelete(SetUndoSnapshotOptions setUnDoSnapshot)
        {
            if (!this.ActionsAllowed)
            {
                return(false);
            }

            if (this.editorState.IsRootNodeSelected)
            {
                return(false);                                     // The root node is to be deleted:  Not allowed
            }
            if (setUnDoSnapshot == SetUndoSnapshotOptions.Yes)
            {
                this.editorState.UndoHandler.SetSnapshot("delete", this.editorState.CursorRaw);
            }

            var optimized = this.editorState.CursorRaw;
            await optimized.OptimizeSelection();

            var deleteResult = await XmlCursorSelectionHelper.DeleteSelection(optimized);

            if (deleteResult.Success)
            {
                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);
            }
            else
            {
                return(false);
            }
        }
        /// <summary>
        /// Copies the current selection to the clipboard
        /// </summary>
        public virtual async Task <bool> ActionCopyToClipboard()
        {
            if (!this.ActionsAllowed)
            {
                return(false);
            }

            var content = await XmlCursorSelectionHelper.GetSelectionAsString(this.editorState.CursorRaw);

            if (string.IsNullOrEmpty(content))
            {
                return(false);                               //Nothing selected
            }
            try
            {
                await this.nativePlatform.Clipboard.Clear();

                await this.nativePlatform.Clipboard.SetText(content); // Copy selection as text to clipboard
            }
            catch (Exception)
            {
                return(false);
            }
            return(true);
        }
        /// <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);
        }
Example #6
0
        /// <summary>
        /// Optimizes the selected area
        /// </summary>
        public async Task OptimizeSelection()
        {
            // Define exchange buffer variables
            XmlCursorPositions dummyPos;
            int dummyTextPos;

            if (StartPos.ActualNode == null)
            {
                return;
            }

            // 1. if the start pos is behind the end pos, then swap both
            if (StartPos.ActualNode == EndPos.ActualNode)  // Both nodes are equal
            {
                if (StartPos.PosOnNode > EndPos.PosOnNode) //  If StartPos is within a node behind EndPos
                {
                    // exchange both positions at the same node
                    dummyPos     = StartPos.PosOnNode;
                    dummyTextPos = StartPos.PosInTextNode;
                    StartPos.SetPos(EndPos.ActualNode, EndPos.PosOnNode, EndPos.PosInTextNode);
                    EndPos.SetPos(EndPos.ActualNode, dummyPos, dummyTextPos);
                }
                else // StartPos was not behind Endpos
                {
                    // Is a text part within a text node selected ?
                    if ((StartPos.PosOnNode == XmlCursorPositions.CursorInsideTextNode) && (EndPos.PosOnNode == XmlCursorPositions.CursorInsideTextNode))
                    {                                                      // A part of a text node is selected
                        if (StartPos.PosInTextNode > EndPos.PosInTextNode) // If the TextStartpos is behind the TextEndpos, then change
                        {                                                  // Exchange text selection
                            dummyTextPos = StartPos.PosInTextNode;
                            StartPos.SetPos(StartPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, EndPos.PosInTextNode);
                            EndPos.SetPos(StartPos.ActualNode, XmlCursorPositions.CursorInsideTextNode, dummyTextPos);
                        }
                    }
                }
            }
            else // Both nodes are not equal
            {
                // If the nodes are wrong in the order, then swap both
                if (ToolboxXml.Node1LaisBeforeNode2(EndPos.ActualNode, StartPos.ActualNode))
                {
                    var tempPos = this.StartPos.Clone();
                    this.StartPos.SetPos(this.EndPos.ActualNode, this.EndPos.PosOnNode, this.EndPos.PosInTextNode);
                    this.EndPos.SetPos(tempPos.ActualNode, tempPos.PosOnNode, tempPos.PosInTextNode);
                }

                // If the EndNode is in the StartNode, select the entire surrounding StartNode
                if (ToolboxXml.IsChild(EndPos.ActualNode, StartPos.ActualNode))
                {
                    await SetPositions(StartPos.ActualNode, XmlCursorPositions.CursorOnNodeStartTag, 0, throwChangedEventWhenValuesChanged : false);
                }

                // Find the first common parent of start and end and select the nodes in this height.
                // This leads to the fact that e.g. with LI elements and UL only the whole LI
                // is selected when dragging the selection over several LI and not only parts of it
                if (StartPos.ActualNode.ParentNode != EndPos.ActualNode.ParentNode) // if start and end are not directly in the same parent
                {
                    // - first find out which is the deepest common parent of start and end node
                    XmlNode commonParent = XmlCursorSelectionHelper.DeepestCommonParent(StartPos.ActualNode, EndPos.ActualNode);
                    // - then upscale start- and end-node to before the parent
                    XmlNode nodeStart = StartPos.ActualNode;
                    while (nodeStart.ParentNode != commonParent)
                    {
                        nodeStart = nodeStart.ParentNode;
                    }
                    XmlNode nodeEnde = EndPos.ActualNode;
                    while (nodeEnde.ParentNode != commonParent)
                    {
                        nodeEnde = nodeEnde.ParentNode;
                    }
                    // - finally show the new start and end nodes
                    StartPos.SetPos(nodeStart, XmlCursorPositions.CursorOnNodeStartTag);
                    EndPos.SetPos(nodeEnde, XmlCursorPositions.CursorOnNodeStartTag);
                }
            }
        }
Example #7
0
        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);
        }
Example #8
0
        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());
        }