/// <summary> /// Finds the degenerate range nearest to a screen coordinate. /// </summary> /// <param name="location">The location in screen coordinates. /// The provider should check that the coordinates are within the client /// area of the provider, and should throw an InvalidOperation exception /// if they are not.</param> /// <returns>A degenerate range nearest the specified location.</returns> ITextRangeProvider ITextProvider.RangeFromPoint(Point location) { TextRangeAdaptor range = null; ITextView textView = GetUpdatedTextView(); if (textView != null) { // Convert the screen point to the element space coordinates. location = ScreenToClient(location, textView.RenderScope); ITextPointer position = textView.GetTextPositionFromPoint(location, true); if (position != null) { range = new TextRangeAdaptor(this, position, position, _textPeer); } } if (range == null) { throw new ArgumentException(SR.Get(SRID.TextProvider_InvalidPoint)); } return range; }
/// <summary> /// Retrieves the range of a child object. /// </summary> /// <param name="childElementProvider">The child element. A provider should check that the /// passed element is a child of the text container, and should throw an /// InvalidOperationException if it is not.</param> /// <returns>A range that spans the child element.</returns> ITextRangeProvider ITextProvider.RangeFromChild(IRawElementProviderSimple childElementProvider) { if (childElementProvider == null) { throw new ArgumentNullException("childElementProvider"); } // Retrieve DependencyObject from AutomationElement DependencyObject childElement; if (_textPeer is TextAutomationPeer) { childElement = ((TextAutomationPeer)_textPeer).ElementFromProvider(childElementProvider); } else { childElement = ((ContentTextAutomationPeer)_textPeer).ElementFromProvider(childElementProvider); } TextRangeAdaptor range = null; if (childElement != null) { ITextPointer rangeStart = null; ITextPointer rangeEnd = null; // Retrieve start and end positions for given element. // If element is TextElement, retrieve its Element Start and End positions. // If element is UIElement hosted by UIContainer (Inlien of Block), // retrieve content Start and End positions of the container. // Otherwise scan ITextContainer to find a range for given element. if (childElement is TextElement) { rangeStart = ((TextElement)childElement).ElementStart; rangeEnd = ((TextElement)childElement).ElementEnd; } else { DependencyObject parent = LogicalTreeHelper.GetParent(childElement); if (parent is InlineUIContainer || parent is BlockUIContainer) { rangeStart = ((TextElement)parent).ContentStart; rangeEnd = ((TextElement)parent).ContentEnd; } else { ITextPointer position = _textContainer.Start.CreatePointer(); while (position.CompareTo(_textContainer.End) < 0) { TextPointerContext context = position.GetPointerContext(LogicalDirection.Forward); if (context == TextPointerContext.ElementStart) { if (childElement == position.GetAdjacentElement(LogicalDirection.Forward)) { rangeStart = position.CreatePointer(LogicalDirection.Forward); position.MoveToElementEdge(ElementEdge.AfterEnd); rangeEnd = position.CreatePointer(LogicalDirection.Backward); break; } } else if (context == TextPointerContext.EmbeddedElement) { if (childElement == position.GetAdjacentElement(LogicalDirection.Forward)) { rangeStart = position.CreatePointer(LogicalDirection.Forward); position.MoveToNextContextPosition(LogicalDirection.Forward); rangeEnd = position.CreatePointer(LogicalDirection.Backward); break; } } position.MoveToNextContextPosition(LogicalDirection.Forward); } } } // Create range if (rangeStart != null && rangeEnd != null) { range = new TextRangeAdaptor(this, rangeStart, rangeEnd, _textPeer); } } if (range == null) { throw new InvalidOperationException(SR.Get(SRID.TextProvider_InvalidChildElement)); } return range; }
/// <summary> /// Searches for an occurrence of text within the range. /// </summary> /// <param name="text">The text to search for.</param> /// <param name="backward">true if the last occurring range should be returned instead of the first.</param> /// <param name="ignoreCase">true if case should be ignored for the purposes of comparison.</param> /// <returns>A subrange with the specified text, or null if no such subrange exists.</returns> ITextRangeProvider ITextRangeProvider.FindText(string text, bool backward, bool ignoreCase) { if (text == null) { throw new ArgumentNullException("text"); } if (text.Length == 0) { throw new ArgumentException(SR.Get(SRID.TextRangeProvider_EmptyStringParameter, "text")); } Normalize(); if (_start.CompareTo(_end) == 0) { return null; } TextRangeAdaptor range = null; FindFlags findFlags = FindFlags.None; if (!ignoreCase) { findFlags |= FindFlags.MatchCase; } if (backward) { findFlags |= FindFlags.FindInReverse; } ITextRange findResult = TextFindEngine.Find(_start, _end, text, findFlags, CultureInfo.CurrentCulture); if (findResult != null && !findResult.IsEmpty) { range = new TextRangeAdaptor(_textAdaptor, findResult.Start, findResult.End, _textPeer); } return range; }
/// <summary> /// Retrieves the visible ranges of text. /// </summary> /// <returns>The ranges of text that are visible, or possibly an empty array if there is /// no visible text whatsoever. Text in the range may still be obscured by an overlapping /// window. Also, portions /// of the range at the beginning, in the middle, or at the end may not be visible /// because they are scrolled off to the side. /// Providers should ensure they return at most a range from the beginning of the first /// line with portions visible through the end of the last line with portions visible.</returns> ITextRangeProvider[] ITextProvider.GetVisibleRanges() { ITextRangeProvider[] ranges = null; ITextView textView = GetUpdatedTextView(); if (textView != null) { List<TextSegment> visibleTextSegments = new List<TextSegment>(); // Get visible portion of the document. // if (textView is MultiPageTextView) { // For MultiPageTextView assume that all current pages are entirely visible. visibleTextSegments.AddRange(textView.TextSegments); } else { // For all others TextViews get visible rectangle and hittest TopLeft and // BottomRight points to retrieve visible range. // Find out the bounds of the area visible through all nested scroll areas Rect visibleRect = GetVisibleRectangle(textView); if (!visibleRect.IsEmpty) { ITextPointer visibleStart = textView.GetTextPositionFromPoint(visibleRect.TopLeft, true); ITextPointer visibleEnd = textView.GetTextPositionFromPoint(visibleRect.BottomRight, true); visibleTextSegments.Add(new TextSegment(visibleStart, visibleEnd, true)); } } // Create collection of TextRangeProviders for visible ranges. if (visibleTextSegments.Count > 0) { ranges = new ITextRangeProvider[visibleTextSegments.Count]; for (int i = 0; i < visibleTextSegments.Count; i++) { ranges[i] = new TextRangeAdaptor(this, visibleTextSegments[i].Start, visibleTextSegments[i].End, _textPeer); } } } // If no text is visible in the control, return the degenerate text range // (empty range) at the beginning of the document. if (ranges == null) { ranges = new ITextRangeProvider[] { new TextRangeAdaptor(this, _textContainer.Start, _textContainer.Start, _textPeer) }; } return ranges; }
/// <summary> /// Searches for a subrange of text that has the specified attribute. To search the entire document use the text provider's /// document range. /// </summary> /// <param name="attributeId">The attribute to search for.</param> /// <param name="value">The value of the specified attribute to search for.</param> /// <param name="backward">true if the last occurring range should be returned instead of the first.</param> /// <returns>A subrange with the specified attribute, or null if no such subrange exists.</returns> ITextRangeProvider ITextRangeProvider.FindAttribute(int attributeId, object value, bool backward) { AutomationTextAttribute attribute = AutomationTextAttribute.LookupById(attributeId); if (attribute == null) { throw new ArgumentNullException("attributeId"); } if (value == null) { throw new ArgumentNullException("value"); } if (!_textPatternAttributes.ContainsKey(attribute)) { return null; } Normalize(); ITextRangeProvider resultRange = null; ITextPointer attrStart = null; ITextPointer attrEnd = null; TextAttributeHelper attr = (TextAttributeHelper)_textPatternAttributes[attribute]; if (backward) { ITextPointer stop = _start; ITextPointer position = _end.CreatePointer(LogicalDirection.Backward); // Go backward from the range end position until we find a position that // has our attribute or we hit the start position of the search range. attrStart = stop; while (position.CompareTo(stop) > 0) { if (attr.AreEqual(value, attr.GetValueAt(position))) { if (attrEnd == null) { attrEnd = position.CreatePointer(LogicalDirection.Backward); } } else { if (attrEnd != null) { attrStart = position.CreatePointer(LogicalDirection.Forward); break; } } if (!position.MoveToNextContextPosition(LogicalDirection.Backward)) { break; } } } else { ITextPointer stop = _end; ITextPointer position = _start.CreatePointer(LogicalDirection.Forward); // Go backward from the range end position until we find a position that // has our attribute or we hit the start position of the search range. attrEnd = stop; while (position.CompareTo(stop) < 0) { if (attr.AreEqual(value, attr.GetValueAt(position))) { if (attrStart == null) { attrStart = position.CreatePointer(LogicalDirection.Forward); } } else { if (attrStart != null) { attrEnd = position.CreatePointer(LogicalDirection.Backward); break; } } if (!position.MoveToNextContextPosition(LogicalDirection.Forward)) { break; } } } if (attrStart != null && attrEnd != null) { resultRange = new TextRangeAdaptor(_textAdaptor, attrStart, attrEnd, _textPeer); } return resultRange; }