Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / TextAdaptor.cs / 1 / TextAdaptor.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//
// Description: Text Object Models Text pattern provider
// Spec for TextPattern at http://team/sites/uiauto/Shared%20Documents/TextPatternSpecM8.doc
// Spec for Text Object Model (TOM) at http://avalon/uis/TextBox%20and%20RichTextBox/Text%20Object%20Model.doc
//
// History:
// 03/15/2004 : mmccr - created
// 09/07/2004 : vsmirnov - refactored
// 01/20/2004 : grzegorz - refactored
//
//---------------------------------------------------------------------------
using System; // Exception
using System.Collections.Generic; // List
using System.Collections.ObjectModel; // ReadOnlyCollection
using System.Security; // SecurityCritical, ...
using System.Windows; // PresentationSource
using System.Windows.Automation; // SupportedTextSelection
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Automation.Provider; // ITextProvider
using System.Windows.Controls.Primitives; // IScrollInfo
using System.Windows.Documents; // ITextContainer
using System.Windows.Media; // Visual
using MS.Internal.Documents; // MultiPageTextView
namespace MS.Internal.Automation
{
///
/// Represents a text provider that supports the text pattern across Text Object
/// Model based Text Controls.
///
internal class TextAdaptor : ITextProvider, IDisposable
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
///
/// Constructor
///
/// Automation Peer representing element for the ui scope of the text
/// ITextContainer
internal TextAdaptor(AutomationPeer textPeer, ITextContainer textContainer)
{
Invariant.Assert(textContainer != null, "Invalid ITextContainer");
Invariant.Assert(textPeer is TextAutomationPeer || textPeer is ContentTextAutomationPeer, "Invalid AutomationPeer");
_textPeer = textPeer;
_textContainer = textContainer;
_textContainer.Changed += new TextContainerChangedEventHandler(OnTextContainerChanged);
if (_textContainer.TextSelection != null)
{
_textContainer.TextSelection.Changed += new EventHandler(OnTextSelectionChanged);
}
}
///
/// Dispose.
///
public void Dispose()
{
if (_textContainer != null && _textContainer.TextSelection != null)
{
_textContainer.TextSelection.Changed -= new EventHandler(OnTextSelectionChanged);
}
}
#endregion Constructors
//--------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
///
/// Retrieves the bounding rectangles for the text lines of a given range.
///
/// Start of range to measure
/// End of range to measure
/// Specifies whether the caller wants the full bounds (false) or the bounds of visible portions
/// of the viewable line only ('true')
/// Requests the results in screen coordinates
/// 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.
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 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 rectangles = new List();
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();
}
///
/// Retrieves associated TextView. If TextView is not valid, tries to update its layout.
///
internal ITextView GetUpdatedTextView()
{
ITextView textView = _textContainer.TextView;
if (textView != null)
{
if (!textView.IsValid)
{
if (!textView.Validate())
{
textView = null;
}
if (textView != null && !textView.IsValid)
{
textView = null;
}
}
}
return textView;
}
///
/// Changes text selection on the element
///
/// Start of range to select
/// End of range to select
/// Automation clients as well as the internal caller of this method (a TextRangeAdapter object) are supposed
/// to verify whether the provider supports text selection by calling SupportsTextSelection first.
/// The internal caller is responsible for raising an InvalidOperationException upon the Automation client' attempt
/// to change selection when it's not supported by the provider
internal void Select(ITextPointer start, ITextPointer end)
{
// Update the selection range
if (_textContainer.TextSelection != null)
{
_textContainer.TextSelection.Select(start, end);
}
}
///
/// This helper method is used by TextRangeAdaptor to bring the range into view
/// through multiple nested scroll providers.
///
internal void ScrollIntoView(ITextPointer start, ITextPointer end, bool alignToTop)
{
// Calculate the bounding rectangle for the range
Rect rangeBounds = Rect.Empty;
Rect[] lineBounds = GetBoundingRectangles(start, end, false, false);
foreach (Rect rect in lineBounds)
{
rangeBounds.Union(rect);
}
ITextView textView = GetUpdatedTextView();
if (textView != null && !rangeBounds.IsEmpty)
{
// Find out the visible portion of the range.
Rect visibleRect = GetVisibleRectangle(textView);
Rect rangeVisibleBounds = Rect.Intersect(rangeBounds, visibleRect);
if (rangeVisibleBounds == rangeBounds)
{
// The range is already in the view. It's probably not aligned as requested,
// but who cares since it's entirely visible anyway.
return;
}
// Ensure the visibility of the range.
// BringIntoView will do most of the magic except the very first scroll
// in order to satisfy the requested alignment.
UIElement renderScope = textView.RenderScope;
Visual visual = renderScope;
while (visual != null)
{
IScrollInfo isi = visual as IScrollInfo;
if (isi != null)
{
// Transform the bounding rectangle into the IScrollInfo coordinates.
if (visual != renderScope)
{
GeneralTransform childToParent = renderScope.TransformToAncestor(visual);
rangeBounds = childToParent.TransformBounds(rangeBounds);
}
if (isi.CanHorizontallyScroll)
{
isi.SetHorizontalOffset(alignToTop ? rangeBounds.Left : (rangeBounds.Right - isi.ViewportWidth));
}
if (isi.CanVerticallyScroll)
{
isi.SetVerticalOffset(alignToTop ? rangeBounds.Top : (rangeBounds.Bottom - isi.ViewportHeight));
}
break;
}
visual = VisualTreeHelper.GetParent(visual) as Visual;
}
FrameworkElement fe = renderScope as FrameworkElement;
if (fe != null)
{
fe.BringIntoView(rangeVisibleBounds);
}
}
else
{
// If failed to retrive range bounds, try to Bring into view closes element.
ITextPointer pointer = alignToTop ? start.CreatePointer() : end.CreatePointer();
pointer.MoveToElementEdge(alignToTop ? ElementEdge.AfterStart : ElementEdge.AfterEnd);
FrameworkContentElement element = pointer.GetAdjacentElement(LogicalDirection.Backward) as FrameworkContentElement;
if (element != null)
{
element.BringIntoView();
}
}
}
#endregion Internal Methods
//--------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------
#region Private Methods
///
/// Notify about content changes.
///
private void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e)
{
_textPeer.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextChanged);
}
///
/// Notify about selection changes.
///
private void OnTextSelectionChanged(object sender, EventArgs e)
{
_textPeer.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextSelectionChanged);
}
///
/// Computes the bounds of the render scope area visible through all nested scroll areas.
///
private Rect GetVisibleRectangle(ITextView textView)
{
Rect visibleRect = new Rect(textView.RenderScope.RenderSize);
Visual visual = VisualTreeHelper.GetParent(textView.RenderScope) as Visual;
while (visual != null && visibleRect != Rect.Empty)
{
if (VisualTreeHelper.GetClip(visual) != null)
{
GeneralTransform transform = textView.RenderScope.TransformToAncestor(visual).Inverse;
// Safer version of transform to descendent (doing the inverse ourself),
// we want the rect inside of our space. (Which is always rectangular and much nicer to work with).
if (transform != null)
{
Rect rectBounds = VisualTreeHelper.GetClip(visual).Bounds;
rectBounds = transform.TransformBounds(rectBounds);
visibleRect.Intersect(rectBounds);
}
else
{
// No visibility if non-invertable transform exists.
visibleRect = Rect.Empty;
}
}
visual = VisualTreeHelper.GetParent(visual) as Visual;
}
return visibleRect;
}
///
/// Convert a point from "client" coordinate space of a window into
/// the coordinate space of the screen.
///
///
/// Critical: This code calls into PresentationSource to get HwndSource
/// TreatAsSafe: This code is not exposing any critical information, at the same time
///
[SecurityCritical, SecurityTreatAsSafe]
private Point ClientToScreen(Point point, Visual visual)
{
PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual);
if (presentationSource != null)
{
GeneralTransform transform = visual.TransformToAncestor(presentationSource.RootVisual);
if (transform != null)
{
point = transform.Transform(point);
}
}
return PointUtil.ClientToScreen(point, presentationSource);
}
///
/// Convert a point from the coordinate space of the screen into
/// the "client" coordinate space of a window.
///
///
/// Critical: This code calls into PresentationSource to get HwndSource
/// TreatAsSafe: This code is not exposing any critical information, at the same time
///
[SecurityCritical, SecurityTreatAsSafe]
private Point ScreenToClient(Point point, Visual visual)
{
PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual);
point = PointUtil.ScreenToClient(point, presentationSource);
if (presentationSource != null)
{
GeneralTransform transform = visual.TransformToAncestor(presentationSource.RootVisual);
if (transform != null)
{
transform = transform.Inverse;
if (transform != null)
{
point = transform.Transform(point);
}
}
}
return point;
}
#endregion Private Methods
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private fields
private AutomationPeer _textPeer;
private ITextContainer _textContainer;
#endregion Private Fields
//-------------------------------------------------------------------
//
// ITextProvider
//
//-------------------------------------------------------------------
#region ITextProvider implementation
///
/// Retrieves the current selection. For providers that have the concept of
/// text selection the provider should implement this method and also return
/// true for the SupportsTextSelection property below. Otherwise this method
/// should throw an InvalidOperation exception.
/// For providers that support multiple disjoint selection, this should return
/// an array of all the currently selected ranges. Providers that don't support
/// multiple disjoint selection should just return an array containing a single
/// range.
///
/// The range of text that is selected, or possibly null if there is
/// no selection.
ITextRangeProvider[] ITextProvider.GetSelection()
{
ITextRange selection = _textContainer.TextSelection;
if (selection == null)
{
throw new InvalidOperationException(SR.Get(SRID.TextProvider_TextSelectionNotSupported));
}
return new ITextRangeProvider[] { new TextRangeAdaptor(this, selection.Start, selection.End, _textPeer) };
}
///
/// Retrieves the visible ranges of text.
///
/// 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.
ITextRangeProvider[] ITextProvider.GetVisibleRanges()
{
ITextRangeProvider[] ranges = null;
ITextView textView = GetUpdatedTextView();
if (textView != null)
{
List visibleTextSegments = new List();
// 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;
}
///
/// Retrieves the range of a child object.
///
/// 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.
/// A range that spans the child element.
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;
}
///
/// Finds the degenerate range nearest to a screen coordinate.
///
/// 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.
/// A degenerate range nearest the specified location.
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;
}
///
/// A text range that encloses the main text of the document. Some auxillary text such as
/// headers, footnotes, or annotations may not be included.
///
ITextRangeProvider ITextProvider.DocumentRange
{
get
{
return new TextRangeAdaptor(this, _textContainer.Start, _textContainer.End, _textPeer);
}
}
///
/// True if the text container supports text selection. If the provider returns false then
/// it should throw InvalidOperation exceptions for ITextProvider.GetSelection and
/// ITextRangeProvider.Select.
///
SupportedTextSelection ITextProvider.SupportedTextSelection
{
get
{
return (_textContainer.TextSelection == null) ? SupportedTextSelection.None : SupportedTextSelection.Single;
}
}
#endregion ITextProvider implementation
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
//
// Description: Text Object Models Text pattern provider
// Spec for TextPattern at http://team/sites/uiauto/Shared%20Documents/TextPatternSpecM8.doc
// Spec for Text Object Model (TOM) at http://avalon/uis/TextBox%20and%20RichTextBox/Text%20Object%20Model.doc
//
// History:
// 03/15/2004 : mmccr - created
// 09/07/2004 : vsmirnov - refactored
// 01/20/2004 : grzegorz - refactored
//
//---------------------------------------------------------------------------
using System; // Exception
using System.Collections.Generic; // List
using System.Collections.ObjectModel; // ReadOnlyCollection
using System.Security; // SecurityCritical, ...
using System.Windows; // PresentationSource
using System.Windows.Automation; // SupportedTextSelection
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Automation.Provider; // ITextProvider
using System.Windows.Controls.Primitives; // IScrollInfo
using System.Windows.Documents; // ITextContainer
using System.Windows.Media; // Visual
using MS.Internal.Documents; // MultiPageTextView
namespace MS.Internal.Automation
{
///
/// Represents a text provider that supports the text pattern across Text Object
/// Model based Text Controls.
///
internal class TextAdaptor : ITextProvider, IDisposable
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
///
/// Constructor
///
/// Automation Peer representing element for the ui scope of the text
/// ITextContainer
internal TextAdaptor(AutomationPeer textPeer, ITextContainer textContainer)
{
Invariant.Assert(textContainer != null, "Invalid ITextContainer");
Invariant.Assert(textPeer is TextAutomationPeer || textPeer is ContentTextAutomationPeer, "Invalid AutomationPeer");
_textPeer = textPeer;
_textContainer = textContainer;
_textContainer.Changed += new TextContainerChangedEventHandler(OnTextContainerChanged);
if (_textContainer.TextSelection != null)
{
_textContainer.TextSelection.Changed += new EventHandler(OnTextSelectionChanged);
}
}
///
/// Dispose.
///
public void Dispose()
{
if (_textContainer != null && _textContainer.TextSelection != null)
{
_textContainer.TextSelection.Changed -= new EventHandler(OnTextSelectionChanged);
}
}
#endregion Constructors
//--------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
///
/// Retrieves the bounding rectangles for the text lines of a given range.
///
/// Start of range to measure
/// End of range to measure
/// Specifies whether the caller wants the full bounds (false) or the bounds of visible portions
/// of the viewable line only ('true')
/// Requests the results in screen coordinates
/// 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.
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 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 rectangles = new List();
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();
}
///
/// Retrieves associated TextView. If TextView is not valid, tries to update its layout.
///
internal ITextView GetUpdatedTextView()
{
ITextView textView = _textContainer.TextView;
if (textView != null)
{
if (!textView.IsValid)
{
if (!textView.Validate())
{
textView = null;
}
if (textView != null && !textView.IsValid)
{
textView = null;
}
}
}
return textView;
}
///
/// Changes text selection on the element
///
/// Start of range to select
/// End of range to select
/// Automation clients as well as the internal caller of this method (a TextRangeAdapter object) are supposed
/// to verify whether the provider supports text selection by calling SupportsTextSelection first.
/// The internal caller is responsible for raising an InvalidOperationException upon the Automation client' attempt
/// to change selection when it's not supported by the provider
internal void Select(ITextPointer start, ITextPointer end)
{
// Update the selection range
if (_textContainer.TextSelection != null)
{
_textContainer.TextSelection.Select(start, end);
}
}
///
/// This helper method is used by TextRangeAdaptor to bring the range into view
/// through multiple nested scroll providers.
///
internal void ScrollIntoView(ITextPointer start, ITextPointer end, bool alignToTop)
{
// Calculate the bounding rectangle for the range
Rect rangeBounds = Rect.Empty;
Rect[] lineBounds = GetBoundingRectangles(start, end, false, false);
foreach (Rect rect in lineBounds)
{
rangeBounds.Union(rect);
}
ITextView textView = GetUpdatedTextView();
if (textView != null && !rangeBounds.IsEmpty)
{
// Find out the visible portion of the range.
Rect visibleRect = GetVisibleRectangle(textView);
Rect rangeVisibleBounds = Rect.Intersect(rangeBounds, visibleRect);
if (rangeVisibleBounds == rangeBounds)
{
// The range is already in the view. It's probably not aligned as requested,
// but who cares since it's entirely visible anyway.
return;
}
// Ensure the visibility of the range.
// BringIntoView will do most of the magic except the very first scroll
// in order to satisfy the requested alignment.
UIElement renderScope = textView.RenderScope;
Visual visual = renderScope;
while (visual != null)
{
IScrollInfo isi = visual as IScrollInfo;
if (isi != null)
{
// Transform the bounding rectangle into the IScrollInfo coordinates.
if (visual != renderScope)
{
GeneralTransform childToParent = renderScope.TransformToAncestor(visual);
rangeBounds = childToParent.TransformBounds(rangeBounds);
}
if (isi.CanHorizontallyScroll)
{
isi.SetHorizontalOffset(alignToTop ? rangeBounds.Left : (rangeBounds.Right - isi.ViewportWidth));
}
if (isi.CanVerticallyScroll)
{
isi.SetVerticalOffset(alignToTop ? rangeBounds.Top : (rangeBounds.Bottom - isi.ViewportHeight));
}
break;
}
visual = VisualTreeHelper.GetParent(visual) as Visual;
}
FrameworkElement fe = renderScope as FrameworkElement;
if (fe != null)
{
fe.BringIntoView(rangeVisibleBounds);
}
}
else
{
// If failed to retrive range bounds, try to Bring into view closes element.
ITextPointer pointer = alignToTop ? start.CreatePointer() : end.CreatePointer();
pointer.MoveToElementEdge(alignToTop ? ElementEdge.AfterStart : ElementEdge.AfterEnd);
FrameworkContentElement element = pointer.GetAdjacentElement(LogicalDirection.Backward) as FrameworkContentElement;
if (element != null)
{
element.BringIntoView();
}
}
}
#endregion Internal Methods
//--------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------
#region Private Methods
///
/// Notify about content changes.
///
private void OnTextContainerChanged(object sender, TextContainerChangedEventArgs e)
{
_textPeer.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextChanged);
}
///
/// Notify about selection changes.
///
private void OnTextSelectionChanged(object sender, EventArgs e)
{
_textPeer.RaiseAutomationEvent(AutomationEvents.TextPatternOnTextSelectionChanged);
}
///
/// Computes the bounds of the render scope area visible through all nested scroll areas.
///
private Rect GetVisibleRectangle(ITextView textView)
{
Rect visibleRect = new Rect(textView.RenderScope.RenderSize);
Visual visual = VisualTreeHelper.GetParent(textView.RenderScope) as Visual;
while (visual != null && visibleRect != Rect.Empty)
{
if (VisualTreeHelper.GetClip(visual) != null)
{
GeneralTransform transform = textView.RenderScope.TransformToAncestor(visual).Inverse;
// Safer version of transform to descendent (doing the inverse ourself),
// we want the rect inside of our space. (Which is always rectangular and much nicer to work with).
if (transform != null)
{
Rect rectBounds = VisualTreeHelper.GetClip(visual).Bounds;
rectBounds = transform.TransformBounds(rectBounds);
visibleRect.Intersect(rectBounds);
}
else
{
// No visibility if non-invertable transform exists.
visibleRect = Rect.Empty;
}
}
visual = VisualTreeHelper.GetParent(visual) as Visual;
}
return visibleRect;
}
///
/// Convert a point from "client" coordinate space of a window into
/// the coordinate space of the screen.
///
///
/// Critical: This code calls into PresentationSource to get HwndSource
/// TreatAsSafe: This code is not exposing any critical information, at the same time
///
[SecurityCritical, SecurityTreatAsSafe]
private Point ClientToScreen(Point point, Visual visual)
{
PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual);
if (presentationSource != null)
{
GeneralTransform transform = visual.TransformToAncestor(presentationSource.RootVisual);
if (transform != null)
{
point = transform.Transform(point);
}
}
return PointUtil.ClientToScreen(point, presentationSource);
}
///
/// Convert a point from the coordinate space of the screen into
/// the "client" coordinate space of a window.
///
///
/// Critical: This code calls into PresentationSource to get HwndSource
/// TreatAsSafe: This code is not exposing any critical information, at the same time
///
[SecurityCritical, SecurityTreatAsSafe]
private Point ScreenToClient(Point point, Visual visual)
{
PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual);
point = PointUtil.ScreenToClient(point, presentationSource);
if (presentationSource != null)
{
GeneralTransform transform = visual.TransformToAncestor(presentationSource.RootVisual);
if (transform != null)
{
transform = transform.Inverse;
if (transform != null)
{
point = transform.Transform(point);
}
}
}
return point;
}
#endregion Private Methods
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private fields
private AutomationPeer _textPeer;
private ITextContainer _textContainer;
#endregion Private Fields
//-------------------------------------------------------------------
//
// ITextProvider
//
//-------------------------------------------------------------------
#region ITextProvider implementation
///
/// Retrieves the current selection. For providers that have the concept of
/// text selection the provider should implement this method and also return
/// true for the SupportsTextSelection property below. Otherwise this method
/// should throw an InvalidOperation exception.
/// For providers that support multiple disjoint selection, this should return
/// an array of all the currently selected ranges. Providers that don't support
/// multiple disjoint selection should just return an array containing a single
/// range.
///
/// The range of text that is selected, or possibly null if there is
/// no selection.
ITextRangeProvider[] ITextProvider.GetSelection()
{
ITextRange selection = _textContainer.TextSelection;
if (selection == null)
{
throw new InvalidOperationException(SR.Get(SRID.TextProvider_TextSelectionNotSupported));
}
return new ITextRangeProvider[] { new TextRangeAdaptor(this, selection.Start, selection.End, _textPeer) };
}
///
/// Retrieves the visible ranges of text.
///
/// 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.
ITextRangeProvider[] ITextProvider.GetVisibleRanges()
{
ITextRangeProvider[] ranges = null;
ITextView textView = GetUpdatedTextView();
if (textView != null)
{
List visibleTextSegments = new List();
// 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;
}
///
/// Retrieves the range of a child object.
///
/// 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.
/// A range that spans the child element.
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;
}
///
/// Finds the degenerate range nearest to a screen coordinate.
///
/// 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.
/// A degenerate range nearest the specified location.
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;
}
///
/// A text range that encloses the main text of the document. Some auxillary text such as
/// headers, footnotes, or annotations may not be included.
///
ITextRangeProvider ITextProvider.DocumentRange
{
get
{
return new TextRangeAdaptor(this, _textContainer.Start, _textContainer.End, _textPeer);
}
}
///
/// True if the text container supports text selection. If the provider returns false then
/// it should throw InvalidOperation exceptions for ITextProvider.GetSelection and
/// ITextRangeProvider.Select.
///
SupportedTextSelection ITextProvider.SupportedTextSelection
{
get
{
return (_textContainer.TextSelection == null) ? SupportedTextSelection.None : SupportedTextSelection.Single;
}
}
#endregion ITextProvider implementation
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ZipIOExtraFieldZip64Element.cs
- List.cs
- QilSortKey.cs
- DbExpressionVisitor_TResultType.cs
- MetadataArtifactLoaderCompositeFile.cs
- SQlBooleanStorage.cs
- Method.cs
- DocumentXPathNavigator.cs
- SBCSCodePageEncoding.cs
- DBDataPermissionAttribute.cs
- BamlRecordWriter.cs
- ApplicationActivator.cs
- StrokeNode.cs
- EngineSiteSapi.cs
- TabletCollection.cs
- DesignerTransactionCloseEvent.cs
- DataPagerFieldCollection.cs
- PageCatalogPartDesigner.cs
- ListCardsInFileRequest.cs
- DoubleConverter.cs
- SecurityPolicySection.cs
- StylusPointCollection.cs
- VirtualPathProvider.cs
- DelegatingConfigHost.cs
- Query.cs
- FontDialog.cs
- EndpointAddressMessageFilter.cs
- XMLUtil.cs
- HMACSHA256.cs
- UrlPath.cs
- ExpressionNode.cs
- ShaperBuffers.cs
- UIHelper.cs
- Rfc2898DeriveBytes.cs
- FormViewDeleteEventArgs.cs
- SystemDiagnosticsSection.cs
- ClockGroup.cs
- ListControlBoundActionList.cs
- ApplicationBuildProvider.cs
- PersonalizationEntry.cs
- EpmSyndicationContentDeSerializer.cs
- WebPartTransformerAttribute.cs
- ContentPlaceHolder.cs
- GAC.cs
- Material.cs
- DataSvcMapFileSerializer.cs
- NullableFloatAverageAggregationOperator.cs
- ToolStripGrip.cs
- FormConverter.cs
- TrustManagerPromptUI.cs
- FacetDescriptionElement.cs
- MatrixStack.cs
- ProcessInfo.cs
- WebSysDisplayNameAttribute.cs
- RowParagraph.cs
- HtmlControlPersistable.cs
- EntityDesignerDataSourceView.cs
- Message.cs
- MobileUITypeEditor.cs
- ListDictionaryInternal.cs
- ApplicationInterop.cs
- DelegateSerializationHolder.cs
- ReferentialConstraintRoleElement.cs
- Itemizer.cs
- FastPropertyAccessor.cs
- AcceleratedTokenAuthenticator.cs
- SuppressIldasmAttribute.cs
- LicenseContext.cs
- RectAnimation.cs
- MetadataArtifactLoaderResource.cs
- CompatibleIComparer.cs
- Point3DAnimationUsingKeyFrames.cs
- _PooledStream.cs
- BindingExpressionUncommonField.cs
- ImageListStreamer.cs
- RoleGroup.cs
- WpfPayload.cs
- CqlBlock.cs
- FontFaceLayoutInfo.cs
- BitmapEffect.cs
- AppDomainFactory.cs
- PackageProperties.cs
- ZipIOExtraFieldElement.cs
- Selection.cs
- AlignmentXValidation.cs
- DeclarativeConditionsCollection.cs
- ImageListDesigner.cs
- NotImplementedException.cs
- TrackingProvider.cs
- ModifierKeysValueSerializer.cs
- WindowsGraphicsWrapper.cs
- ActivityExecutor.cs
- QueryOutputWriterV1.cs
- CodeEventReferenceExpression.cs
- DataTableMapping.cs
- DataGridViewCellStyle.cs
- SelectionEditingBehavior.cs
- ProgressChangedEventArgs.cs
- Thread.cs
- OperationResponse.cs