Exemplo n.º 1
0
        /// <summary>
        /// </summary>
        void ITextSelection.SetCaretToPosition(ITextPointer caretPosition, LogicalDirection direction, bool allowStopAtLineEnd, bool allowStopNearSpace)
        {
            // We need a pointer with appropriate direction,
            // becasue it will be used in textRangeBase.Select method for
            // pointer normalization.
            caretPosition = caretPosition.CreatePointer(direction);

            // Normalize the position in its logical direction - to get to text content over there.
            caretPosition.MoveToInsertionPosition(direction);

            // We need a pointer with the reverse direction to confirm
            // the line wrapping position. So we can ensure Bidi caret navigation.
            // Bidi can have the different caret position by setting the
            // logical direction, so we have to only set the logical direction
            // as the forward for the real line wrapping position.
            ITextPointer reversePosition = caretPosition.CreatePointer(direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward);

            // Check line wrapping condition
            if (!allowStopAtLineEnd &&
                ((TextPointerBase.IsAtLineWrappingPosition(caretPosition, this.TextView) &&
                  TextPointerBase.IsAtLineWrappingPosition(reversePosition, this.TextView)) ||
                 TextPointerBase.IsNextToPlainLineBreak(caretPosition, LogicalDirection.Backward) ||
                 TextSchema.IsBreak(caretPosition.GetElementType(LogicalDirection.Backward))))
            {
                // Caret is at wrapping position, and we are not allowed to stay at end of line,
                // so we choose forward direction to appear in the begiinning of a second line
                caretPosition.SetLogicalDirection(LogicalDirection.Forward);
            }
            else
            {
                if (caretPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text &&
                    caretPosition.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    // This is statistically most typical case. No "smartness" needed
                    // to choose standard Forward orientation for the caret.
                    // NOTE: By using caretPosition's direction we solve BiDi caret orientation case:
                    // The orietnation reflects a direction from where caret has been moved
                    // or orientation where mouse clicked. So we will stick with appropriate
                    // character.

                    // Nothing to do. The caretPosition is good to go.
                }
                else if (!allowStopNearSpace)
                {
                    // There are some tags around, and we are not allowed to choose a side near to space.
                    // So we need to perform some content analysis.

                    char[] charBuffer = new char[1];

                    if (caretPosition.GetPointerContext(direction) == TextPointerContext.Text &&
                        caretPosition.GetTextInRun(direction, charBuffer, 0, 1) == 1 &&
                        Char.IsWhiteSpace(charBuffer[0]))
                    {
                        LogicalDirection oppositeDirection = direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward;

                        // Check formatting switch condition at this position
                        FlowDirection initialFlowDirection = (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty);

                        bool moved = caretPosition.MoveToInsertionPosition(oppositeDirection);

                        if (moved &&
                            initialFlowDirection == (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty) &&
                            (caretPosition.GetPointerContext(oppositeDirection) != TextPointerContext.Text ||
                             caretPosition.GetTextInRun(oppositeDirection, charBuffer, 0, 1) != 1 ||
                             !Char.IsWhiteSpace(charBuffer[0])))
                        {
                            // In the opposite direction we have a non-space
                            // character. So we choose that direction
                            direction = oppositeDirection;
                            caretPosition.SetLogicalDirection(direction);
                        }
                    }
                }
            }

            // Now that orientation of a caretPosition is identified,
            // build an empty selection at this position
            TextRangeBase.BeginChange(this);
            try
            {
                TextRangeBase.Select(this, caretPosition, caretPosition);

                // Note how Select method works for the case of empty range:
                // It creates a single instance TextPointer normalized and oriented
                // in a direction taken from caretPosition:
                ITextSelection thisSelection = this;
                Invariant.Assert(thisSelection.Start.LogicalDirection == caretPosition.LogicalDirection); // orientation must be as passed
                Invariant.Assert(this.IsEmpty);
                //Invariant.Assert((object)thisSelection.Start == (object)thisSelection.End); // it must be the same instance of TextPointer
                //Invariant.Assert(TextPointerBase.IsAtInsertionPosition(thisSelection.Start, caretPosition.LogicalDirection)); // normalization must be done in the same diredction as orientation

                // Clear active positions when selection is empty
                SetActivePositions(null, null);
            }
            finally
            {
                TextRangeBase.EndChange(this);
            }
        }
        // <see cref="System.Windows.Documents.TextPointer.MoveToLineBoundary"/>
        internal static int MoveToLineBoundary(ITextPointer thisPointer, ITextView textView, int count, bool respectNonMeargeableInlineStart)
        {
            ITextPointer position;
            double newSuggestedX;

            Invariant.Assert(!thisPointer.IsFrozen, "Can't reposition a frozen pointer!");
            Invariant.Assert(textView != null, "Null TextView!"); // Did you check ITextPointer.HasValidLayout?

            position = textView.GetPositionAtNextLine(thisPointer, Double.NaN, count, out newSuggestedX, out count);

            if (!position.IsAtInsertionPosition)
            {
                if (!respectNonMeargeableInlineStart || 
                    (!IsAtNonMergeableInlineStart(position) && !IsAtNonMergeableInlineEnd(position)))
                {
                    position.MoveToInsertionPosition(position.LogicalDirection);
                }
            }

            if (IsAtRowEnd(position))
            {
                // We will find outselves at a row end when we have incomplete
                // markup like
                //
                //  <TableCell></TableCell> <!-- No inner Run! -->
                //
                // In that case the end-of-row is the entire line.
                thisPointer.MoveToPosition(position);
                thisPointer.SetLogicalDirection(position.LogicalDirection);
            }
            else
            {
                TextSegment lineRange = textView.GetLineRange(position);

                if (!lineRange.IsNull)
                {
                    thisPointer.MoveToPosition(lineRange.Start);
                    thisPointer.SetLogicalDirection(lineRange.Start.LogicalDirection);
                }
                else if (count > 0)
                {
                    // It is possible to get a non-zero return value from ITextView.GetPositionAtNextLine
                    // when moving into a BlockUIContainer.  The container is the "next line" but does
                    // not contain any lines itself -- GetLineRange will return null.
                    thisPointer.MoveToPosition(position);
                    thisPointer.SetLogicalDirection(position.LogicalDirection);
                }
            }

            return count;
        }
Exemplo n.º 3
0
        /// <summary>
        /// Moves the position to the closes unit boundary.
        /// </summary>
        private bool MoveToUnitBoundary(ITextPointer position, bool isStart, LogicalDirection direction, TextUnit unit)
        {
            bool moved = false;
            ITextView textView;
            switch (unit)
            {
                case TextUnit.Character:
                    if (!TextPointerBase.IsAtInsertionPosition(position))
                    {
                        if (TextPointerBase.MoveToNextInsertionPosition(position, direction))
                        {
                            moved = true;
                        }
                    }
                    break;

                case TextUnit.Word:
                    if (!IsAtWordBoundary(position))
                    {
                        if (MoveToNextWordBoundary(position, direction))
                        {
                            moved = true;
                        }
                    }
                    break;

                case TextUnit.Format:
                    // Formatting changes can be introduced by elements. Hence it is fair to 
                    // assume that formatting boundaries are defined by non-text context.
                    while (position.GetPointerContext(direction) == TextPointerContext.Text)
                    {
                        if (position.MoveToNextContextPosition(direction))
                        {
                            moved = true;
                        }
                    }
                    // Make sure we end with text on the right, so that later ExpandToEnclosingUnit calls
                    // do the right thing.
                    if (moved && direction == LogicalDirection.Forward)
                    {
                        while (true)
                        {
                            TextPointerContext context = position.GetPointerContext(LogicalDirection.Forward);

                            if (context != TextPointerContext.ElementStart && context != TextPointerContext.ElementEnd)
                                break;

                            position.MoveToNextContextPosition(LogicalDirection.Forward);
                        }
                    }
                    break;

                case TextUnit.Line:
                    // Positions are snapped to closest line boundaries. But since line information
                    // is based on the layout, positions are not changed, if:
                    // a) they are not currently in the view, or
                    // b) containing line cannot be found.
                    textView = _textAdaptor.GetUpdatedTextView();
                    if (textView != null && textView.IsValid && textView.Contains(position))
                    {
                        TextSegment lineRange = textView.GetLineRange(position);
                        if (!lineRange.IsNull)
                        {
                            double newSuggestedX;
                            int linesMoved = 0;

                            if (direction == LogicalDirection.Forward)
                            {
                                ITextPointer nextLineStart = null;

                                if (isStart)
                                {
                                    nextLineStart = textView.GetPositionAtNextLine(lineRange.End, Double.NaN, 1, out newSuggestedX, out linesMoved);
                                }

                                if (linesMoved != 0)
                                {
                                    lineRange = textView.GetLineRange(nextLineStart);
                                    nextLineStart = lineRange.Start;
                                }
                                else
                                {
                                    nextLineStart = lineRange.End;
                                }
                                nextLineStart = GetInsertionPosition(nextLineStart, LogicalDirection.Forward);

                                if (position.CompareTo(nextLineStart) != 0)
                                {
                                    position.MoveToPosition(nextLineStart);
                                    position.SetLogicalDirection(isStart ? LogicalDirection.Forward : LogicalDirection.Backward);
                                    moved = true;
                                }
                            }
                            else
                            {
                                ITextPointer previousLineEnd = null;

                                if (!isStart)
                                {
                                    previousLineEnd = textView.GetPositionAtNextLine(lineRange.Start, Double.NaN, -1, out newSuggestedX, out linesMoved);
                                }

                                if (linesMoved != 0)
                                {
                                    lineRange = textView.GetLineRange(previousLineEnd);
                                    previousLineEnd = lineRange.End;
                                }
                                else
                                {
                                    previousLineEnd = lineRange.Start;
                                }
                                previousLineEnd = GetInsertionPosition(previousLineEnd, LogicalDirection.Backward);

                                if (position.CompareTo(previousLineEnd) != 0)
                                {
                                    position.MoveToPosition(previousLineEnd);
                                    position.SetLogicalDirection(isStart ? LogicalDirection.Forward : LogicalDirection.Backward);
                                    moved = true;
                                }
                            }
                        }
                    }
                    break;

                case TextUnit.Paragraph:
                    // Utilize TextRange logic to determine paragraph boundaries.
                    ITextRange textRange = new TextRange(position, position);
                    TextRangeBase.SelectParagraph(textRange, position);

                    if (direction == LogicalDirection.Forward)
                    {
                        ITextPointer nextParagraphStart = textRange.End;

                        if (isStart)
                        {
                            nextParagraphStart = nextParagraphStart.CreatePointer();
                            if (nextParagraphStart.MoveToNextInsertionPosition(LogicalDirection.Forward))
                            {
                                TextRangeBase.SelectParagraph(textRange, nextParagraphStart);
                                nextParagraphStart = textRange.Start;
                            }
                        }

                        if (position.CompareTo(nextParagraphStart) != 0)
                        {
                            position.MoveToPosition(nextParagraphStart);
                            position.SetLogicalDirection(isStart ? LogicalDirection.Forward : LogicalDirection.Backward);
                            moved = true;
                        }
                    }
                    else
                    {
                        ITextPointer previousParagraphEnd = textRange.Start;

                        if (!isStart)
                        {
                            previousParagraphEnd = previousParagraphEnd.CreatePointer();
                            if (previousParagraphEnd.MoveToNextInsertionPosition(LogicalDirection.Backward))
                            {
                                TextRangeBase.SelectParagraph(textRange, previousParagraphEnd);
                                previousParagraphEnd = textRange.End;
                            }
                        }

                        if (position.CompareTo(previousParagraphEnd) != 0)
                        {
                            position.MoveToPosition(previousParagraphEnd);
                            position.SetLogicalDirection(isStart ? LogicalDirection.Forward : LogicalDirection.Backward);
                            moved = true;
                        }
                    }
                    break;

                case TextUnit.Page:
                    // Positions are snapped to nearest page boundaries. But since page information
                    // is based on the layout, positions are not changed, if they are not currently in the view.
                    // We need to consider 2 types of scenarios: single page and multi-page.
                    // In case of multi-page scenario, first need to find a page associated with the position.
                    // If page is found, move the start position to the beginning of the first range of that page
                    // and move the end position to the end of the last range of that page.
                    textView = _textAdaptor.GetUpdatedTextView();
                    if (textView != null && textView.IsValid && textView.Contains(position))
                    {
                        ITextView pageTextView = textView;
                        if (textView is MultiPageTextView)
                        {
                            // This is "multi page" case. Find page associated with the start position.
                            pageTextView = ((MultiPageTextView)textView).GetPageTextViewFromPosition(position);
                        }
                        ReadOnlyCollection<TextSegment> textSegments = pageTextView.TextSegments;

                        if (textSegments != null && textSegments.Count > 0)
                        {
                            //When comparing, we need to take into account if the pointer is not right at 
                            //the end of the page (or beginning) because of normalization
                            
                            if (direction == LogicalDirection.Forward)
                            {
                                while (position.CompareTo(textSegments[textSegments.Count - 1].End) != 0)
                                {
                                    if (position.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.ElementEnd)
                                    {
                                        position.MoveToPosition(textSegments[textSegments.Count - 1].End);
                                        moved = true;
                                        break;
                                    }
                                    Invariant.Assert(position.MoveToNextContextPosition(LogicalDirection.Forward));
                                }
                                MoveToInsertionPosition(position, LogicalDirection.Forward);
                            }
                            else
                            {
                                while (position.CompareTo(textSegments[0].Start) != 0)
                                {
                                    if (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.ElementStart)
                                    {
                                        position.MoveToPosition(textSegments[0].Start);
                                        moved = true;
                                        break;
                                    }
                                    Invariant.Assert(position.MoveToNextContextPosition(LogicalDirection.Backward));
                                }
                                MoveToInsertionPosition(position, LogicalDirection.Backward);
                                
                            }
                        }
                    }
                    break;

                case TextUnit.Document:
                    if (direction == LogicalDirection.Forward)
                    {
                        if (position.CompareTo(GetInsertionPosition(position.TextContainer.End, LogicalDirection.Backward)) != 0)
                        {
                            position.MoveToPosition(position.TextContainer.End);
                            moved = true;
                        }
                    }
                    else
                    {
                        if (position.CompareTo(GetInsertionPosition(position.TextContainer.Start, LogicalDirection.Forward)) != 0)
                        {
                            position.MoveToPosition(position.TextContainer.Start);
                            moved = true;
                        }
                    }
                    break;

                default:
                    // Unknown unit
                    break;
            }
            return moved;
        }
Exemplo n.º 4
0
        /// <summary>
        /// Re-positions the given position by an integral number of text units, but it does
        /// not guarantee that position is snapped to TextUnit boundary.
        /// This method assumes that input position is already snapped to appropriate TextUnit boundary.
        /// </summary>
        /// <param name="position">The position to move</param>
        /// <param name="unit">Text units to step by</param>
        /// <param name="count">Number of units to step over. Also specifies the direction of moving: 
        /// forward if positive, backward otherwise</param>
        /// <returns>The actual number of units the position was moved over</returns>
        private int MovePositionByUnits(ITextPointer position, TextUnit unit, int count)
        {
            ITextView textView;
            int moved = 0;
            int absCount = (count == int.MinValue) ? int.MaxValue : Math.Abs(count);
            LogicalDirection direction = (count > 0) ? LogicalDirection.Forward : LogicalDirection.Backward;

            // This method assumes that position is already snapped to appropriate TextUnit.

            switch (unit)
            {
                case TextUnit.Character:
                    while (moved < absCount)
                    {
                        if (!TextPointerBase.MoveToNextInsertionPosition(position, direction))
                        {
                            break;
                        }
                        moved++;
                    }
                    break;

                case TextUnit.Word:
                    while (moved < absCount)
                    {
                        if (!MoveToNextWordBoundary(position, direction))
                        {
                            break;
                        }
                        moved++;
                    }
                    break;

                case TextUnit.Format:
                    // Formatting changes can be introduced by elements. Hence it is fair to 
                    // assume that formatting boundaries are defined by non-text context.
                    while (moved < absCount)
                    {
                        ITextPointer positionOrig = position.CreatePointer();

                        // First skip all text in given direction.
                        while (position.GetPointerContext(direction) == TextPointerContext.Text)
                        {
                            if (!position.MoveToNextContextPosition(direction))
                            {
                                break;
                            }
                        }
                        // Move to next context
                        if (!position.MoveToNextContextPosition(direction))
                        {
                            break;
                        }
                        // Skip all formatting elements and position the pointer next to text.
                        while (position.GetPointerContext(direction) != TextPointerContext.Text)
                        {
                            if (!position.MoveToNextContextPosition(direction))
                            {
                                break;
                            }
                        }
                        // If moving backwards, position the pointer at the beginning of formatting range.
                        if (direction == LogicalDirection.Backward)
                        {
                            while (position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text)
                            {
                                if (!position.MoveToNextContextPosition(LogicalDirection.Backward))
                                {
                                    break;
                                }
                            }
                        }
                        if (position.GetPointerContext(direction) != TextPointerContext.None)
                        {
                            moved++;
                        }
                        else
                        {
                            position.MoveToPosition(positionOrig);
                            break;
                        }
                    }

                    // Adjust logical direction to point to the following text (forward or backward movement).
                    // If we don't do this, we'll normalize in the wrong direction and get stuck in a loop
                    // if caller tries to advance again.
                    position.SetLogicalDirection(LogicalDirection.Forward);

                    break;

                case TextUnit.Line:
                    // Position is snapped to nearest line boundary. But since line information
                    // is based on the layout, position is not changed, if:
                    // a) it is not currently in the view, or
                    // b) containing line cannot be found.
                    textView = _textAdaptor.GetUpdatedTextView();
                    if (textView != null && textView.IsValid && textView.Contains(position))
                    {
                        // ITextPointer.MoveToLineBoundary can't handle Table row end positions.
                        // Mimic TextEditor's caret navigation code and move into the preceding
                        // TableCell.
                        if (TextPointerBase.IsAtRowEnd(position))
                        {
                            position.MoveToNextInsertionPosition(LogicalDirection.Backward);
                        }

                        moved = position.MoveToLineBoundary(count);
                        
                        MoveToInsertionPosition(position, LogicalDirection.Forward);

                        if (moved < 0)
                        {
                            moved = -moved; // Will be reversed below.
                        }
                    }
                    break; 

                case TextUnit.Paragraph:
                    // Utilize TextRange logic to determine paragraph boundaries.
                    ITextRange paragraphRange = new TextRange(position, position);
                    paragraphRange.SelectParagraph(position);
                    while (moved < absCount)
                    {
                        position.MoveToPosition(direction == LogicalDirection.Forward ? paragraphRange.End : paragraphRange.Start);
                        if (!position.MoveToNextInsertionPosition(direction))
                        {
                            break;
                        }
                        moved++;
                        paragraphRange.SelectParagraph(position);
                        position.MoveToPosition(paragraphRange.Start); // Position it always at the beginning of the paragraph.
                    }
                    break;

                case TextUnit.Page:
                    // But since page information is based on the layout, position is not changed, if:
                    // a) it is not currently in the view, or
                    // b) containing page cannot be found.
                    // Page movement is possible only in multi-page scenario.
                    textView = _textAdaptor.GetUpdatedTextView();
                    if (textView != null && textView.IsValid && textView.Contains(position))
                    {
                        if (textView is MultiPageTextView)
                        {
                            // Get embedded page ITextView for given position.
                            ITextView pageTextView = ((MultiPageTextView)textView).GetPageTextViewFromPosition(position);
                            ReadOnlyCollection<TextSegment> textSegments = pageTextView.TextSegments;
                            while (moved < absCount)
                            {
                                if (textSegments == null || textSegments.Count == 0)
                                {
                                    break;
                                }
                                // Move the position to appropriate edge.
                                if (direction == LogicalDirection.Backward)
                                {
                                    position.MoveToPosition(textSegments[0].Start);
                                    MoveToInsertionPosition(position, LogicalDirection.Backward);
                                }
                                else
                                {
                                    position.MoveToPosition(textSegments[textSegments.Count - 1].End);
                                    MoveToInsertionPosition(position, LogicalDirection.Forward);
                                }
                                // Try to move the position to the next page.
                                ITextPointer positionTemp = position.CreatePointer();
                                if (!positionTemp.MoveToNextInsertionPosition(direction))
                                {
                                    break;
                                }
                                else
                                {
                                    // MoveToNextInsertionPosition may return 'true' and move the position
                                    // in oposite direction.
                                    if (direction == LogicalDirection.Forward)
                                    {
                                        if (positionTemp.CompareTo(position) <= 0)
                                        {
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        if (positionTemp.CompareTo(position) >= 0)
                                        {
                                            break;
                                        }
                                    }
                                }
                                // Get embedded page ITextView for given position.
                                if (!textView.Contains(positionTemp))
                                {
                                    break;
                                }
                                pageTextView = ((MultiPageTextView)textView).GetPageTextViewFromPosition(positionTemp);
                                textSegments = pageTextView.TextSegments;
                                moved++;
                            }
                        }
                    }
                    break;

                case TextUnit.Document:
                    // This method assumes that position is already snapped to appropriate TextUnit.
                    break;
            }

            return (direction == LogicalDirection.Forward) ? moved : -moved;
        }
Exemplo n.º 5
0
        /// <summary>
        /// Helper function to move given position to word boundary. TextPointerBase.MoveToNextWordBoundary 
        /// cannot be used directly, because it does not modify LogicalDirection. Because of that, IsAtWordBoundary
        /// for just moved positions may return FALSE.
        /// </summary>
        private static bool MoveToNextWordBoundary(ITextPointer position, LogicalDirection direction)
        {
            int moveCounter = 0;
            ITextPointer startPosition = position.CreatePointer();

            // Move the position in the given direction until word boundary is reached.
            while (position.MoveToNextInsertionPosition(direction))
            {
                moveCounter++;
                if (IsAtWordBoundary(position))
                {
                    break;
                }
                // Need to break the loop for weird case when there is no word break in text content.
                // When the word looks too long, consider end of textRun as a word break.
                if (moveCounter > 128) // 128 was taken as a random number. Probably not big enough though...
                {
                    position.MoveToPosition(startPosition);
                    position.MoveToNextContextPosition(direction);
                    break;
                }
            }

            // Note that we always use Forward direction for word orientation.
            if (moveCounter > 0)
            {
                position.SetLogicalDirection(LogicalDirection.Forward);
            }
            return moveCounter > 0;
        }