/// <summary> /// Gets start and end offset for a text segment but clamps those values to the start and end /// of a given element. This way if a large text range is being resolved on a node that only contains /// a portion of the text range (such as a paragraph) the result only includes the content in that node. /// </summary> private void GetTextSegmentValues(TextSegment segment, ITextPointer elementStart, ITextPointer elementEnd, out int startOffset, out int endOffset) { startOffset = 0; endOffset = 0; if (elementStart.CompareTo(segment.Start) >= 0) { // segment starts before the start of the element startOffset = 0; } else { startOffset = elementStart.GetOffsetToPosition(segment.Start); } if (elementEnd.CompareTo(segment.End) >= 0) { endOffset = elementStart.GetOffsetToPosition(segment.End); } else { // segment ends after the end of the element endOffset = elementStart.GetOffsetToPosition(elementEnd); } }
/// <summary> /// Returns a TextSegment that spans the line on which position is located. /// </summary> /// <param name="paragraph">Paragraph to search</param> /// <param name="position">Any oriented text position on the line.</param> private TextSegment GetLineRangeFromPosition(ParagraphResult paragraph, ITextPointer position) { TextSegment lineRange = TextSegment.Null; // Each paragraph type is handled differently: // a) ContainerParagraph - process nested paragraphs. // b) TextParagraph - find line index of a line containing input text position // and then return its range. // c) TableParagraph - process nested paragraphs. if (paragraph is ContainerParagraphResult) { // a) ContainerParagraph - process nested paragraphs. ReadOnlyCollection<ParagraphResult> nestedParagraphs = ((ContainerParagraphResult)paragraph).Paragraphs; // Paragraphs collection may be null in case of empty List. Invariant.Assert(nestedParagraphs != null, "Paragraph collection is null."); if (nestedParagraphs.Count > 0) { lineRange = GetLineRangeFromPosition(nestedParagraphs, _emptyParagraphCollection, position); } } else if (paragraph is TextParagraphResult) { // b) TextParagraph - find line index of a line containing input text position // and then return its range. ReadOnlyCollection<LineResult> lines = ((TextParagraphResult)paragraph).Lines; Invariant.Assert(lines != null, "Lines collection is null"); if (!((TextParagraphResult)paragraph).HasTextContent) { // Paragraph has no lines. // lineRange = new TextSegment(((TextParagraphResult)paragraph).EndPosition, ((TextParagraphResult)paragraph).EndPosition, true); } else { // Get index of the line that contains position. int lineIndex = TextParagraphView.GetLineFromPosition(lines, position); Invariant.Assert(lineIndex >= 0 && lineIndex < lines.Count, "Line not found."); lineRange = new TextSegment(lines[lineIndex].StartPosition, lines[lineIndex].GetContentEndPosition(), true); } } else if (paragraph is TableParagraphResult) { // c) TableParagraph - process nested paragraphs. ReadOnlyCollection<ParagraphResult> nestedParagraphs = ((TableParagraphResult)paragraph).GetParagraphsFromPosition(position); // Paragraphs collection may be null in case of empty List. Invariant.Assert(nestedParagraphs != null, "Paragraph collection is null."); if (nestedParagraphs.Count > 0) { lineRange = GetLineRangeFromPosition(nestedParagraphs, _emptyParagraphCollection, position); } } else if (paragraph is SubpageParagraphResult) { SubpageParagraphResult subpageParagraphResult = (SubpageParagraphResult)paragraph; ReadOnlyCollection<ColumnResult> columns = subpageParagraphResult.Columns; ReadOnlyCollection<ParagraphResult> nestedFloatingElements = subpageParagraphResult.FloatingElements; Invariant.Assert(columns != null, "Column collection is null."); Invariant.Assert(nestedFloatingElements != null, "Paragraph collection is null."); if (columns.Count > 0 || nestedFloatingElements.Count > 0) { lineRange = GetLineRangeFromPosition(columns, nestedFloatingElements, position); } } else if (paragraph is FigureParagraphResult) { FigureParagraphResult figureParagraphResult = (FigureParagraphResult)paragraph; ReadOnlyCollection<ColumnResult> columns = figureParagraphResult.Columns; ReadOnlyCollection<ParagraphResult> nestedFloatingElements = figureParagraphResult.FloatingElements; Invariant.Assert(columns != null, "Column collection is null."); Invariant.Assert(nestedFloatingElements != null, "Paragraph collection is null."); if (columns.Count > 0 || nestedFloatingElements.Count > 0) { lineRange = GetLineRangeFromPosition(columns, nestedFloatingElements, position); } } else if (paragraph is FloaterParagraphResult) { FloaterParagraphResult floaterParagraphResult = (FloaterParagraphResult)paragraph; ReadOnlyCollection<ColumnResult> columns = floaterParagraphResult.Columns; ReadOnlyCollection<ParagraphResult> nestedFloatingElements = floaterParagraphResult.FloatingElements; Invariant.Assert(columns != null, "Column collection is null."); Invariant.Assert(nestedFloatingElements != null, "Paragraph collection is null."); if (columns.Count > 0 || nestedFloatingElements.Count > 0) { lineRange = GetLineRangeFromPosition(columns, nestedFloatingElements, position); } } else if (paragraph is UIElementParagraphResult) { // UIElement paragraph result - return content range between BlockUIContainer.ContentStart and ContentEnd BlockUIContainer blockUIContainer = paragraph.Element as BlockUIContainer; if (blockUIContainer != null) { lineRange = new TextSegment(blockUIContainer.ContentStart.CreatePointer(LogicalDirection.Forward), blockUIContainer.ContentEnd.CreatePointer(LogicalDirection.Backward)); } } return lineRange; }
// Part of ExtendSelectionByMouse method: // Identifies words on selection ends. private void IdentifyWordsOnSelectionEnds(ITextPointer anchorPosition, ITextPointer cursorPosition, bool forceWordSelection, out TextSegment anchorWordRange, out TextSegment cursorWordRange) { if (forceWordSelection) { anchorWordRange = TextPointerBase.GetWordRange(anchorPosition); cursorWordRange = TextPointerBase.GetWordRange(cursorPosition, cursorPosition.LogicalDirection); } else { // Define whether word adjustment is allowed. Pressing Shift+Control prevents from auto-word expansion. bool disableWordExpansion = _textEditor.AutoWordSelection == false || ((Keyboard.Modifiers & ModifierKeys.Shift) != 0 && (Keyboard.Modifiers & ModifierKeys.Control) != 0); if (disableWordExpansion) { anchorWordRange = new TextSegment(anchorPosition, anchorPosition); cursorWordRange = new TextSegment(cursorPosition, cursorPosition); } else { // Autoword expansion heuristics // ----------------------------- // Word autoword heuristics: // a) After active end returned to selected area, autoword expansion on active end is disabled // b) After active end returned to the very first word, expansion on anchor word is disabled either // We do this though only if selection has crossed initial word boundary at least once. // c) After active end crosses new word, autoword expansion of active end is enabled again // Calculate a word range for anchor position anchorWordRange = TextPointerBase.GetWordRange(anchorPosition); // Check if we re-entering selection or moving outside // and set autoexpansion flags accordingly if (_previousCursorPosition != null && (anchorPosition.CompareTo(cursorPosition) < 0 && cursorPosition.CompareTo(_previousCursorPosition) < 0 || _previousCursorPosition.CompareTo(cursorPosition) < 0 && cursorPosition.CompareTo(anchorPosition) < 0)) { // Re-entering selection. // Store position of reentering _reenterPosition = cursorPosition.CreatePointer(); // When re-entering reaches initial word, disable word expansion on anchor end either if (_anchorWordRangeHasBeenCrossedOnce && anchorWordRange.Contains(cursorPosition)) { _allowWordExpansionOnAnchorEnd = false; } } else { // Extending the selection. // Check if we are crossing a boundary of last reentered word to re-enable word expansion on moving end if (_reenterPosition != null) { TextSegment lastReenteredWordRange = TextPointerBase.GetWordRange(_reenterPosition); if (!lastReenteredWordRange.Contains(cursorPosition)) { _reenterPosition = null; } } } // Identify expanded range on both ends // if (anchorWordRange.Contains(cursorPosition) || anchorWordRange.Contains(cursorPosition.GetInsertionPosition(LogicalDirection.Forward)) || anchorWordRange.Contains(cursorPosition.GetInsertionPosition(LogicalDirection.Backward))) { // Selection does not cross word boundary, so shrink selection to exact anchor/cursor positions anchorWordRange = new TextSegment(anchorPosition, anchorPosition); cursorWordRange = new TextSegment(cursorPosition, cursorPosition); } else { // Selection crosses word boundary. _anchorWordRangeHasBeenCrossedOnce = true; if (!_allowWordExpansionOnAnchorEnd || // TextPointerBase.IsAtWordBoundary(anchorPosition, /*insideWordDirection:*/LogicalDirection.Forward)) { // We collapse anchorPosition in two cases: // If we have been re-entering the initial word before - // then we treat it as an indicator that user wants exact position on anchor end // or // if selection starts exactly on word boundary - // then we should not include the following word (when selection extends backward). // // So in the both cases we collapse anchorWordRange to exact _anchorPosition anchorWordRange = new TextSegment(anchorPosition, anchorPosition); } if (TextPointerBase.IsAfterLastParagraph(cursorPosition) || TextPointerBase.IsAtWordBoundary(cursorPosition, /*insideWordDirection:*/LogicalDirection.Forward)) { cursorWordRange = new TextSegment(cursorPosition, cursorPosition); } else { if (_reenterPosition == null) { // We are not in re-entering mode; expand moving end to word boundary cursorWordRange = TextPointerBase.GetWordRange(cursorPosition, cursorPosition.LogicalDirection); } else { // We are in re-entering mode; use exact moving end position cursorWordRange = new TextSegment(cursorPosition, cursorPosition); } } } } } }
//----------------------------------------------------- // // Private methods // //----------------------------------------------------- #region Private methods /// <summary> /// Calculates geometry for ine TextSegment /// </summary> /// <param name="geometry">GeometryGroup to add the geometry</param> /// <param name="segment">TextSegment</param> /// <param name="parentView">TextView to which geometry has to be transformed</param> private void GetSegmentGeometry(GeometryGroup geometry, TextSegment segment, ITextView parentView) { List<ITextView> textViews = TextSelectionHelper.GetDocumentPageTextViews(segment); Debug.Assert(textViews != null, "geometry text view not found"); foreach (ITextView view in textViews) { Geometry viewGeometry = GetPageGeometry(segment, view, parentView); if (viewGeometry != null) geometry.Children.Add(viewGeometry); } }
/// <summary> /// Get a geometry for a particular page and transforms it to the parent page /// </summary> /// <param name="segment">the TextSegment for which geometry we are looking</param> /// <param name="view">the page view</param> /// <param name="parentView">the parent page view</param> /// <returns></returns> private Geometry GetPageGeometry(TextSegment segment, ITextView view, ITextView parentView) { Debug.Assert((view != null) && (parentView != null), "null text view"); //in the initial layout update the TextViews might be invalid. This is OK //since there will be a second pass if (!view.IsValid || !parentView.IsValid) return null; //Debug.Assert((view.RenderScope != null) && (parentView.RenderScope != null), "null text view render scope"); if ((view.RenderScope == null) || (parentView.RenderScope == null)) return null; Geometry pageGeometry = null; pageGeometry = view.GetTightBoundingGeometryFromTextPositions(segment.Start, segment.End); if (pageGeometry != null) { if (parentView != null) { Transform additionalTransform = (Transform)view.RenderScope.TransformToVisual(parentView.RenderScope); if (pageGeometry.Transform != null) { //we need to create geometry group in this case TransformGroup group = new TransformGroup(); group.Children.Add(pageGeometry.Transform); group.Children.Add(additionalTransform); pageGeometry.Transform = group; } else { //now set the transformation pageGeometry.Transform = additionalTransform; } } } return pageGeometry; }
// Constructor. internal AnnotationHighlightChangedEventArgs(ITextPointer start, ITextPointer end) { TextSegment[] rangeArray = new TextSegment[] { new TextSegment(start, end) }; _ranges = new ReadOnlyCollection<TextSegment>(rangeArray); }
/// <summary> /// Creates a new HighlightSegment with a list of owners /// </summary> /// <param name="start">start segment position</param> /// <param name="end">end segment position</param> /// <param name="owners">owners list</param> private void Init(ITextPointer start, ITextPointer end, IList<IHighlightRange> owners) { Debug.Assert(start != null, "start pointer is null"); Debug.Assert(end != null, "end pointer is null"); Debug.Assert(owners != null, "null owners list"); Debug.Assert(owners.Count > 0, "empty owners list"); for (int i = 0; i < owners.Count; i++) Debug.Assert(owners[i] != null, "null owner"); _segment = new TextSegment(start, end); IsHitTestVisible = false; object textContainer = start.TextContainer; _isFixedContainer = textContainer is FixedTextContainer || textContainer is DocumentSequenceTextContainer; //check for tables, figures and floaters and extract the content GetContent(); }
// Returns a normalized line range from TextView for a given position. // Note: In current contract, line range returned by TextView.GetLineRange() is not guaranteed to be normalized. // This helper does appropriate correction and returns a normalized line range. internal static TextSegment GetNormalizedLineRange(ITextView textView, ITextPointer position) { TextSegment lineRange = textView.GetLineRange(position); if (lineRange.IsNull) { if (!typeof(BlockUIContainer).IsAssignableFrom(position.ParentType)) { return lineRange; } ITextPointer lineStart = position.CreatePointer(LogicalDirection.Forward); lineStart.MoveToElementEdge(ElementEdge.AfterStart); ITextPointer lineEnd = position.CreatePointer(LogicalDirection.Backward); lineEnd.MoveToElementEdge(ElementEdge.BeforeEnd); lineRange = new TextSegment(lineStart, lineEnd); return lineRange; } // Normalize line range ITextRange textRange = new TextRange(lineRange.Start, lineRange.End); return new TextSegment(textRange.Start, textRange.End); }
//----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods /// <summary> /// Checks if the input range overlaps existing segments and splits them if needed /// </summary> /// <param name="highlightRange">range data</param> /// <param name="invalidateStart">start pointer of the invalid area</param> /// <param name="invalidateEnd">end pointer of the invalid area</param> /// <remarks>This method requires ordered nonoverlaped input segments. Otherwise it will assert.</remarks> private void ProcessOverlapingSegments(IHighlightRange highlightRange, out ITextPointer invalidateStart, out ITextPointer invalidateEnd) { Debug.Assert(highlightRange != null, " null highlight range"); ReadOnlyCollection<TextSegment> rangeSegments = highlightRange.Range.TextSegments; Debug.Assert((rangeSegments != null) && (rangeSegments.Count > 0), "invalid rangeSegments"); invalidateStart = null; invalidateEnd = null; int ind = 0; IEnumerator<TextSegment> rangeEnumerator = rangeSegments.GetEnumerator(); TextSegment rangeSegment = rangeEnumerator.MoveNext() ? rangeEnumerator.Current : TextSegment.Null; while ((ind < _segments.Count) && (!rangeSegment.IsNull)) { HighlightSegment highlightSegment = _segments[ind]; Debug.Assert(highlightSegment != null, "null highlight segment"); if (highlightSegment.Segment.Start.CompareTo(rangeSegment.Start) <= 0) { if (highlightSegment.Segment.End.CompareTo(rangeSegment.Start) > 0) { //split highlightSegment //the split method is smart enough to take care of edge cases - point on start/end of the //segment, points outside the segment etc IList<HighlightSegment> res = highlightSegment.Split(rangeSegment.Start, rangeSegment.End, highlightRange); //if the result does not contain the original segment we need to clear the owners if (!res.Contains(highlightSegment)) highlightSegment.ClearOwners(); _segments.Remove(highlightSegment); _segments.InsertRange(ind, res); ind = ind + res.Count - 1; //check if we need to move to next range segment if (rangeSegment.End.CompareTo(highlightSegment.Segment.End) <= 0) { //get next one bool next = rangeEnumerator.MoveNext(); Debug.Assert(rangeEnumerator.Current.IsNull || !next || (rangeSegment.End.CompareTo(rangeEnumerator.Current.Start) <= 0), "overlapped range segments"); rangeSegment = next ? rangeEnumerator.Current : TextSegment.Null; } else { //get the piece that is left rangeSegment = new TextSegment(highlightSegment.Segment.End, rangeSegment.End); } //set invalidateStart if needed if (invalidateStart == null) invalidateStart = highlightSegment.Segment.Start; } else { //move to next highlightsegment ind++; } } else { //set invalidateStart if needed if (invalidateStart == null) invalidateStart = rangeSegment.Start; if (rangeSegment.End.CompareTo(highlightSegment.Segment.Start) > 0) { //add the piece before the highlight segment HighlightSegment temp = new HighlightSegment(rangeSegment.Start, highlightSegment.Segment.Start, highlightRange); _segments.Insert(ind++, temp); //now our current segment is the rest of the range segment rangeSegment = new TextSegment(highlightSegment.Segment.Start, rangeSegment.End); } else { //just insert this range segment - it does not cover any highlight segnments. Increment ind //so it points to the same highlight segment _segments.Insert(ind++, new HighlightSegment(rangeSegment.Start, rangeSegment.End, highlightRange)); //get next range segment rangeSegment = rangeEnumerator.MoveNext() ? rangeEnumerator.Current : TextSegment.Null; } } } // if (!rangeSegment.IsNull) { if (invalidateStart == null) invalidateStart = rangeSegment.Start; _segments.Insert(ind++, new HighlightSegment(rangeSegment.Start, rangeSegment.End, highlightRange)); } //check if there are more rangeSegments while (rangeEnumerator.MoveNext()) { _segments.Insert(ind++, new HighlightSegment(rangeEnumerator.Current.Start, rangeEnumerator.Current.End, highlightRange)); } //set invalidateEnd if (invalidateStart != null) { if (ind == _segments.Count) ind--; invalidateEnd = _segments[ind].Segment.End; } }
// Helper function used in DeleteContent for clearing TableCell contents private static void ClearTableCells(TextSegment textSegment) { TableCell cell = GetTableCellFromPosition((TextPointer)textSegment.Start); TextPointer end = ((TextPointer)textSegment.End).GetNextInsertionPosition(LogicalDirection.Backward); while (cell != null) { cell.Blocks.Clear(); cell.Blocks.Add(new Paragraph()); TextPointer cellEnd = cell.ElementEnd; if (cellEnd.CompareTo(end) < 0 && cellEnd.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { cell = (TableCell)cellEnd.GetAdjacentElement(LogicalDirection.Forward); } else { cell = null; } } }
/// <summary> /// Gets a list of ITextViews spanned by this text segment /// </summary> /// <param name="segment">the text segment</param> /// <returns>the TextViews list</returns> internal static List<ITextView> GetDocumentPageTextViews(TextSegment segment) { List<ITextView> res = null; int startPageNumber, endPageNumber; //revert the logical direction of the pointers ITextPointer start = segment.Start.CreatePointer(LogicalDirection.Forward); ITextPointer end = segment.End.CreatePointer(LogicalDirection.Backward); DependencyObject content = start.TextContainer.Parent as DependencyObject; if (content != null) { FlowDocumentScrollViewer scrollViewer = PathNode.GetParent(content) as FlowDocumentScrollViewer; if (scrollViewer != null) { IServiceProvider provider = scrollViewer.ScrollViewer.Content as IServiceProvider; Invariant.Assert(provider != null, "FlowDocumentScrollViewer should be an IServiceProvider."); res = new List<ITextView>(1); res.Add(provider.GetService(typeof(ITextView)) as ITextView); return res; } } IDocumentPaginatorSource idp = GetPointerPage(start, out startPageNumber); DynamicDocumentPaginator ddp = idp.DocumentPaginator as DynamicDocumentPaginator; endPageNumber = ddp != null ? ddp.GetPageNumber((ContentPosition)end) : -1; if (startPageNumber == -1 || endPageNumber == -1) { // If either page couldn't be found, we return an empty list. This // could be caused by a failure in paginating the document. res = new List<ITextView>(0); } else if (startPageNumber == endPageNumber) { res = ProcessSinglePage(idp, startPageNumber); } else { res = ProcessMultiplePages(idp, startPageNumber, endPageNumber); } return res; }
// Token: 0x06003C06 RID: 15366 RVA: 0x00115000 File Offset: 0x00113200 internal TextSegment(ITextPointer startPosition, ITextPointer endPosition) { this = new TextSegment(startPosition, endPosition, false); }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end) { // Initalize the start/end line pointer ITextPointer startLinePointer = start; ITextPointer endLinePointer = startLinePointer; // Get all composition lines that includes the start/end pointer while (endLinePointer.CompareTo(end) < 0) { TextSegment textSegment = _textView.GetLineRange(endLinePointer); if (textSegment.IsNull) { // endLinePointer is not within the TextView's definition of a line. // Skip ahead to text on the next iteration. startLinePointer = endLinePointer; } else { Debug.Assert(start.CompareTo(startLinePointer) <= 0, "The start pointer is positioned after the composition start line pointer!"); if (startLinePointer.CompareTo(textSegment.Start) < 0) { // Update the start line pointer startLinePointer = textSegment.Start; } if (endLinePointer.CompareTo(textSegment.End) < 0) { if (end.CompareTo(textSegment.End) < 0) { // Update the end line pointer endLinePointer = end.CreatePointer(); } else { // Update the end line pointer endLinePointer = textSegment.End.CreatePointer(LogicalDirection.Backward); } } else { Debug.Assert(endLinePointer.CompareTo(textSegment.End) == 0, "The end line pointer is positioned after the composition text range end pointer!"); } // Get the rectangle for start/end position Rect startRect = _textView.GetRectangleFromTextPosition(startLinePointer); Rect endRect = _textView.GetRectangleFromTextPosition(endLinePointer); // Add the composition line to be rendered _compositionLines.Add(new CompositionLine(startRect, endRect, _textServicesDisplayAttribute.GetLineColor(startLinePointer))); startLinePointer = textSegment.End.CreatePointer(LogicalDirection.Forward); } // Move the start pointer to the next text line. startLinePointer must be a pointer to start // text. while ((startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) && (startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)) { startLinePointer.MoveToNextContextPosition(LogicalDirection.Forward); } endLinePointer = startLinePointer; } }
// Token: 0x0600860E RID: 34318 RVA: 0x0024B92A File Offset: 0x00249B2A public TextRange(TextSegment textSegment) { this = new WinRTSpellerInterop.TextRange((int)textSegment.StartPosition, (int)textSegment.Length); }