/// <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; }
//------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------- #region Internal Methods /// <summary> /// Retrieves the bounding rectangles for the text lines of a given range. /// </summary> /// <param name="start">Start of range to measure</param> /// <param name="end">End of range to measure</param> /// <param name="clipToView">Specifies whether the caller wants the full bounds (false) or the bounds of visible portions /// of the viewable line only ('true')</param> /// <param name="transformToScreen">Requests the results in screen coordinates</param> /// <returns>An array of bounding rectangles for each line or portion of a line within the client area of the text provider. /// No bounding rectangles will be returned for lines that are empty or scrolled out of view. Note that even though a /// bounding rectangle is returned the corresponding text may not be visible due to overlapping windows. /// This will not return null, but may return an empty array.</returns> internal Rect[] GetBoundingRectangles(ITextPointer start, ITextPointer end, bool clipToView, bool transformToScreen) { ITextView textView = GetUpdatedTextView(); if (textView == null) { return(new Rect[0]); } // If start/end positions are not in the visible range, move them to the first/last visible positions. ReadOnlyCollection <TextSegment> textSegments = textView.TextSegments; if (textSegments.Count > 0) { if (!textView.Contains(start) && start.CompareTo(textSegments[0].Start) < 0) { start = textSegments[0].Start.CreatePointer();; } if (!textView.Contains(end) && end.CompareTo(textSegments[textSegments.Count - 1].End) > 0) { end = textSegments[textSegments.Count - 1].End.CreatePointer(); } } if (!textView.Contains(start) || !textView.Contains(end)) { return(new Rect[0]); } TextRangeAdaptor.MoveToInsertionPosition(start, LogicalDirection.Forward); TextRangeAdaptor.MoveToInsertionPosition(end, LogicalDirection.Backward); Rect visibleRect = Rect.Empty; if (clipToView) { visibleRect = GetVisibleRectangle(textView); // If clipping into view and visible rect is empty, return. if (visibleRect.IsEmpty) { return(new Rect[0]); } } List <Rect> rectangles = new List <Rect>(); ITextPointer position = start.CreatePointer(); while (position.CompareTo(end) < 0) { TextSegment lineRange = textView.GetLineRange(position); if (!lineRange.IsNull) { // Since range is limited to just one line, GetTightBoundingGeometry will return tight bounding // rectangle for given range. It will also work correctly with bidi text. ITextPointer first = (lineRange.Start.CompareTo(start) <= 0) ? start : lineRange.Start; ITextPointer last = (lineRange.End.CompareTo(end) >= 0) ? end : lineRange.End; Rect lineRect = Rect.Empty; Geometry geometry = textView.GetTightBoundingGeometryFromTextPositions(first, last); if (geometry != null) { lineRect = geometry.Bounds; if (clipToView) { lineRect.Intersect(visibleRect); } if (!lineRect.IsEmpty) { if (transformToScreen) { lineRect = new Rect(ClientToScreen(lineRect.TopLeft, textView.RenderScope), ClientToScreen(lineRect.BottomRight, textView.RenderScope)); } rectangles.Add(lineRect); } } } if (position.MoveToLineBoundary(1) == 0) { position = end; } } return(rectangles.ToArray()); }
// Token: 0x060065C4 RID: 26052 RVA: 0x001C8254 File Offset: 0x001C6454 internal Rect[] GetBoundingRectangles(ITextPointer start, ITextPointer end, bool clipToView, bool transformToScreen) { ITextView updatedTextView = this.GetUpdatedTextView(); if (updatedTextView == null) { return(new Rect[0]); } ReadOnlyCollection <TextSegment> textSegments = updatedTextView.TextSegments; if (textSegments.Count > 0) { if (!updatedTextView.Contains(start) && start.CompareTo(textSegments[0].Start) < 0) { start = textSegments[0].Start.CreatePointer(); } if (!updatedTextView.Contains(end) && end.CompareTo(textSegments[textSegments.Count - 1].End) > 0) { end = textSegments[textSegments.Count - 1].End.CreatePointer(); } } if (!updatedTextView.Contains(start) || !updatedTextView.Contains(end)) { return(new Rect[0]); } TextRangeAdaptor.MoveToInsertionPosition(start, LogicalDirection.Forward); TextRangeAdaptor.MoveToInsertionPosition(end, LogicalDirection.Backward); Rect rect = Rect.Empty; if (clipToView) { rect = this.GetVisibleRectangle(updatedTextView); if (rect.IsEmpty) { return(new Rect[0]); } } List <Rect> list = new List <Rect>(); ITextPointer textPointer = start.CreatePointer(); while (textPointer.CompareTo(end) < 0) { TextSegment lineRange = updatedTextView.GetLineRange(textPointer); if (!lineRange.IsNull) { ITextPointer startPosition = (lineRange.Start.CompareTo(start) <= 0) ? start : lineRange.Start; ITextPointer endPosition = (lineRange.End.CompareTo(end) >= 0) ? end : lineRange.End; Rect item = Rect.Empty; Geometry tightBoundingGeometryFromTextPositions = updatedTextView.GetTightBoundingGeometryFromTextPositions(startPosition, endPosition); if (tightBoundingGeometryFromTextPositions != null) { item = tightBoundingGeometryFromTextPositions.Bounds; if (clipToView) { item.Intersect(rect); } if (!item.IsEmpty) { if (transformToScreen) { item = new Rect(this.ClientToScreen(item.TopLeft, updatedTextView.RenderScope), this.ClientToScreen(item.BottomRight, updatedTextView.RenderScope)); } list.Add(item); } } } if (textPointer.MoveToLineBoundary(1) == 0) { textPointer = end; } } return(list.ToArray()); }