Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / Annotations / Anchoring / TextSelectionProcessor.cs / 1 / TextSelectionProcessor.cs
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// TextSelectionProcessor uses TextAnchors to represent portions
// of text that are anchors. It produces locator parts that
// represent these TextAnchors and can generate TextAnchors from
// the locator parts.
// Spec: http://team/sites/ag/Specifications/Anchoring%20Namespace%20Spec.doc
//
// History:
// 12/01/2002: magedz: Created - based on architectural discussions and design by axelk, rruiz, magedz
// 04/01/2003: rruiz: Updated file as part of integrating into working system
// 07/21/2003: rruiz: Ported to WCP tree.
// 08/18/2003: rruiz: Updated to Anchoring Namespace Spec.
// 03/29/2004: ssimova: Moved some common code toe TextSelectionHelper
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
using System.Xml;
using MS.Utility;
using MS.Internal.Documents;
namespace MS.Internal.Annotations.Anchoring
{
///
/// TextSelectionProcessor uses TextAnchors to represent portions
/// of text that are anchors. It produces locator parts that
/// represent these TextAnchors and can generate TextAnchors from
/// the locator parts.
///
internal sealed class TextSelectionProcessor : SelectionProcessor
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// Creates an instance of TextSelectionProcessor.
///
public TextSelectionProcessor()
{
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Merges the two anchors into one, if possible.
///
/// anchor to merge
/// other anchor to merge
/// new anchor that contains the data from both
/// anchor1 and anchor2
/// true if the anchors were merged, false otherwise
///
/// anchor1 or anchor2 are
/// null
public override bool MergeSelections(Object anchor1, Object anchor2, out Object newAnchor)
{
return TextSelectionHelper.MergeSelections(anchor1, anchor2, out newAnchor);
}
///
/// Gets the tree elements spanned by the selection.
///
/// the selection to examine
/// a list of elements spanned by the selection; never returns
/// null
/// selection is null
/// selection is of wrong type
public override IList GetSelectedNodes(Object selection)
{
return TextSelectionHelper.GetSelectedNodes(selection);
}
///
/// Gets the parent element of this selection.
///
/// the selection to examine
/// the parent element of the selection; can be null
/// selection is null
/// selection is of wrong type
public override UIElement GetParent(Object selection)
{
return TextSelectionHelper.GetParent(selection);
}
///
/// Gets the anchor point for the selection
///
/// the selection to examine
/// the anchor point of the selection; can be null
/// selection is null
/// selection is of wrong type
public override Point GetAnchorPoint(Object selection)
{
return TextSelectionHelper.GetAnchorPoint(selection);
}
///
/// Creates one or more locator parts representing the portion
/// of 'startNode' spanned by 'selection'.
///
/// the selection that is being processed
/// the node the locator parts should be in the
/// context of
/// one or more locator parts representing the portion of 'startNode' spanned
/// by 'selection'
/// startNode or selection is null
/// selection is of the wrong type
public override IList
GenerateLocatorParts(Object selection, DependencyObject startNode)
{
if (startNode == null)
throw new ArgumentNullException("startNode");
if (selection == null)
throw new ArgumentNullException("selection");
ITextPointer start;
ITextPointer end;
IList textSegments = null;
TextSelectionHelper.CheckSelection(selection, out start, out end, out textSegments);
if (!(start is TextPointer))
throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection");
ITextPointer elementStart;
ITextPointer elementEnd;
// If we can't get the start/end of the node then we can't generate a locator part
if (!GetNodesStartAndEnd(startNode, out elementStart, out elementEnd))
return null;
if (elementStart.CompareTo(end) > 0)
throw new ArgumentException(SR.Get(SRID.InvalidStartNodeForTextSelection), "startNode");
if (elementEnd.CompareTo(start) < 0)
throw new ArgumentException(SR.Get(SRID.InvalidStartNodeForTextSelection), "startNode");
ContentLocatorPart part = new ContentLocatorPart(CharacterRangeElementName);
int startOffset = 0;
int endOffset = 0;
for(int i = 0; i < textSegments.Count; i++)
{
GetTextSegmentValues(textSegments[i], elementStart, elementEnd, out startOffset, out endOffset);
part.NameValuePairs.Add(SegmentAttribute + i.ToString(NumberFormatInfo.InvariantInfo), startOffset.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + endOffset.ToString(NumberFormatInfo.InvariantInfo));
}
part.NameValuePairs.Add(CountAttribute, textSegments.Count.ToString(NumberFormatInfo.InvariantInfo));
List res = new List(1);
res.Add(part);
return res;
}
///
/// Creates a selection object spanning the portion of 'startNode'
/// specified by 'locatorPart'.
///
/// locator part specifying data to be spanned
/// the node to be spanned by the created
/// selection
/// set to AttachmentLevel.Full if the entire range of text
/// was resolved, otherwise set to StartPortion, MiddlePortion, or EndPortion based on
/// which part of the range was resolved
/// a selection spanning the portion of 'startNode' specified by
/// 'locatorPart', null if selection described by locator part could not be
/// recreated
/// locatorPart or startNode are
/// null
/// locatorPart is of the incorrect type
public override Object ResolveLocatorPart(ContentLocatorPart locatorPart, DependencyObject startNode, out AttachmentLevel attachmentLevel)
{
if (startNode == null)
throw new ArgumentNullException("startNode");
if (locatorPart == null)
throw new ArgumentNullException("locatorPart");
if (CharacterRangeElementName != locatorPart.PartType)
throw new ArgumentException(SR.Get(SRID.IncorrectLocatorPartType, locatorPart.PartType.Namespace + ":" + locatorPart.PartType.Name), "locatorPart");
// First we extract the offset and length of the
// text range from the locator part.
int startOffset = 0;
int endOffset = 0;
string stringCount = locatorPart.NameValuePairs[CountAttribute];
if (stringCount == null)
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute));
int count = Int32.Parse(stringCount,NumberFormatInfo.InvariantInfo);
TextAnchor anchor = new TextAnchor();
attachmentLevel = AttachmentLevel.Unresolved;
for (int i = 0; i < count; i++)
{
GetLocatorPartSegmentValues(locatorPart, i, out startOffset, out endOffset);
// Now we grab the TextRange so we can create a selection.
// TextBox doesn't expose its internal TextRange so we use
// its API for creating and getting the selection.
ITextPointer elementStart;
ITextPointer elementEnd;
// If we can't get the start/end of the node then we can't resolve the locator part
if (!GetNodesStartAndEnd(startNode, out elementStart, out elementEnd))
return null;
// If the offset is not withing the element's text range we return null
int textRangeLength = elementStart.GetOffsetToPosition(elementEnd);
if (startOffset > textRangeLength)
return null;
ITextPointer start = elementStart.CreatePointer(startOffset);// new TextPointer((TextPointer)elementStart, startOffset);
ITextPointer end = (textRangeLength <= endOffset) ?
elementEnd.CreatePointer() : //new TextPointer((TextPointer)elementEnd) :
elementStart.CreatePointer(endOffset);// new TextPointer((TextPointer)elementStart, endOffset);
//we do not process 0 length selection
if (start.CompareTo(end) >= 0)
return null;
anchor.AddTextSegment(start, end);
}
//we do not support 0 or negative length selection
if (anchor.IsEmpty)
{
throw new ArgumentException(SR.Get(SRID.IncorrectAnchorLength), "locatorPart");
}
attachmentLevel = AttachmentLevel.Full;
if (_clamping)
{
ITextPointer currentStart = anchor.Start;
ITextPointer currentEnd = anchor.End;
IServiceProvider serviceProvider = null;
ITextView textView = null;
if (_targetPage != null)
{
serviceProvider = _targetPage as IServiceProvider;
}
else
{
FlowDocument content = currentStart.TextContainer.Parent as FlowDocument;
serviceProvider = PathNode.GetParent(content as DependencyObject) as IServiceProvider;
}
Invariant.Assert(serviceProvider != null, "No ServiceProvider found to get TextView from.");
textView = serviceProvider.GetService(typeof(ITextView)) as ITextView;
Invariant.Assert(textView != null, "Null TextView provided by ServiceProvider.");
anchor = TextAnchor.TrimToIntersectionWith(anchor, textView.TextSegments);
if (anchor == null)
{
attachmentLevel = AttachmentLevel.Unresolved;
}
else
{
if (anchor.Start.CompareTo(currentStart) != 0)
{
attachmentLevel &= ~AttachmentLevel.StartPortion;
}
if (anchor.End.CompareTo(currentEnd) != 0)
{
attachmentLevel &= ~AttachmentLevel.EndPortion;
}
}
}
return anchor;
}
///
/// Returns a list of XmlQualifiedNames representing the
/// the locator parts this processor can resolve/generate.
///
public override XmlQualifiedName[] GetLocatorPartTypes()
{
return (XmlQualifiedName[])LocatorPartTypeNames.Clone();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Operators
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Public Events
//
//-----------------------------------------------------
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// Controls whether or not resolving should clamp the text
/// anchors to the visible portion of a text container. Default
/// value is true.
///
internal bool Clamping
{
set
{
_clamping = value;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
///
/// Gets the smallest offset and the largest offset from all the segments defined in the locator part.
///
internal static void GetMaxMinLocatorPartValues(ContentLocatorPart locatorPart, out int startOffset, out int endOffset)
{
if (locatorPart == null)
throw new ArgumentNullException("locatorPart");
string stringCount = locatorPart.NameValuePairs[CountAttribute];
if (stringCount == null)
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute));
int count = Int32.Parse(stringCount,NumberFormatInfo.InvariantInfo);
startOffset = Int32.MaxValue;
endOffset = 0;
int segStart;
int segEnd;
for (int i = 0; i < count; i++)
{
GetLocatorPartSegmentValues(locatorPart, i, out segStart, out segEnd);
if (segStart < startOffset)
startOffset = segStart;
if (segEnd > endOffset)
endOffset = segEnd;
}
}
///
/// Set the DocumentPageView this selection processor should use
/// when clamping text anchors. If this target is not set then
/// the set of DPVs held by the viewer are used.
///
internal void SetTargetDocumentPageView(DocumentPageView target)
{
Debug.Assert(target != null);
_targetPage = target;
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Fields
//
//-----------------------------------------------------
#region Internal Fields
// Name of segment attribute
internal const String SegmentAttribute = "Segment";
// Name of segment attribute
internal const String CountAttribute = "Count";
// Name added to a LocatorPart with value "true" to mean
// the LocatorPart matches for any overlapping LocatorPart
internal const String IncludeOverlaps = "IncludeOverlaps";
// Potential separators for values in segment name/value pairs
internal static readonly Char[] Separator = new Char[] { ',' };
// Name of locator part element
internal static readonly XmlQualifiedName CharacterRangeElementName = new XmlQualifiedName("CharacterRange", AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
#endregion Internal Fields
//------------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#region Private Methods
///
/// Extracts the values of attributes from a locator part.
///
/// the locator part to extract values from
/// the number of the segment to extract values for
/// value of offset attribute
/// value of length attribute
private static void GetLocatorPartSegmentValues(ContentLocatorPart locatorPart, int segmentNumber, out int startOffset, out int endOffset)
{
if (segmentNumber < 0)
throw new ArgumentException("segmentNumber");
string segmentString = locatorPart.NameValuePairs[SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)];
string[] values = segmentString.Split(Separator);
if (values.Length != 2)
{
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
}
startOffset = Int32.Parse(values[0], NumberFormatInfo.InvariantInfo);
endOffset = Int32.Parse(values[1], NumberFormatInfo.InvariantInfo);
}
///
/// Returns the TextContainer for a node that contains text. If the node
/// doesn't have a TextContainer this method returns null.
///
private ITextContainer GetTextContainer(DependencyObject startNode)
{
Debug.Assert(startNode != null);
ITextContainer textContainer = null;
IServiceProvider serviceProvider = startNode as IServiceProvider;
if (serviceProvider != null)
{
textContainer = serviceProvider.GetService(typeof(ITextContainer)) as ITextContainer;
}
if (textContainer == null)
{
// Special case for TextBox which doesn't implement IServiceProvider
TextBoxBase textBox = startNode as TextBoxBase;
if (textBox != null)
{
textContainer = textBox.TextContainer;
}
}
return textContainer;
}
///
/// Returns ITextPointers positioned at the start and end of an element
/// that contains text.
///
private bool GetNodesStartAndEnd(DependencyObject startNode, out ITextPointer start, out ITextPointer end)
{
start = null;
end = null;
ITextContainer textContainer = GetTextContainer(startNode);
if (textContainer != null)
{
start = textContainer.Start;
end = textContainer.End;
}
else
{
// Special case for TextElement which doesn't expose its TextContainer
TextElement textElement = startNode as TextElement;
if (textElement != null)
{
start = textElement.ContentStart;
end = textElement.ContentEnd;
}
else
{
return false;
}
}
return true;
}
///
/// 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.
///
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);
}
}
#endregion Private Methods
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// ContentLocatorPart types understood by this processor
private static readonly XmlQualifiedName[] LocatorPartTypeNames =
new XmlQualifiedName[]
{
CharacterRangeElementName
};
// Optional DPV - used in printing case when there is no viewer available
private DocumentPageView _targetPage = null;
// Controls whether or not resolving clamps text anchors to
// the visible portion of a TextContainer
private bool _clamping = true;
#endregion Private Fields
}
}
// 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:
// TextSelectionProcessor uses TextAnchors to represent portions
// of text that are anchors. It produces locator parts that
// represent these TextAnchors and can generate TextAnchors from
// the locator parts.
// Spec: http://team/sites/ag/Specifications/Anchoring%20Namespace%20Spec.doc
//
// History:
// 12/01/2002: magedz: Created - based on architectural discussions and design by axelk, rruiz, magedz
// 04/01/2003: rruiz: Updated file as part of integrating into working system
// 07/21/2003: rruiz: Ported to WCP tree.
// 08/18/2003: rruiz: Updated to Anchoring Namespace Spec.
// 03/29/2004: ssimova: Moved some common code toe TextSelectionHelper
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
using System.Xml;
using MS.Utility;
using MS.Internal.Documents;
namespace MS.Internal.Annotations.Anchoring
{
///
/// TextSelectionProcessor uses TextAnchors to represent portions
/// of text that are anchors. It produces locator parts that
/// represent these TextAnchors and can generate TextAnchors from
/// the locator parts.
///
internal sealed class TextSelectionProcessor : SelectionProcessor
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
///
/// Creates an instance of TextSelectionProcessor.
///
public TextSelectionProcessor()
{
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Merges the two anchors into one, if possible.
///
/// anchor to merge
/// other anchor to merge
/// new anchor that contains the data from both
/// anchor1 and anchor2
/// true if the anchors were merged, false otherwise
///
/// anchor1 or anchor2 are
/// null
public override bool MergeSelections(Object anchor1, Object anchor2, out Object newAnchor)
{
return TextSelectionHelper.MergeSelections(anchor1, anchor2, out newAnchor);
}
///
/// Gets the tree elements spanned by the selection.
///
/// the selection to examine
/// a list of elements spanned by the selection; never returns
/// null
/// selection is null
/// selection is of wrong type
public override IList GetSelectedNodes(Object selection)
{
return TextSelectionHelper.GetSelectedNodes(selection);
}
///
/// Gets the parent element of this selection.
///
/// the selection to examine
/// the parent element of the selection; can be null
/// selection is null
/// selection is of wrong type
public override UIElement GetParent(Object selection)
{
return TextSelectionHelper.GetParent(selection);
}
///
/// Gets the anchor point for the selection
///
/// the selection to examine
/// the anchor point of the selection; can be null
/// selection is null
/// selection is of wrong type
public override Point GetAnchorPoint(Object selection)
{
return TextSelectionHelper.GetAnchorPoint(selection);
}
///
/// Creates one or more locator parts representing the portion
/// of 'startNode' spanned by 'selection'.
///
/// the selection that is being processed
/// the node the locator parts should be in the
/// context of
/// one or more locator parts representing the portion of 'startNode' spanned
/// by 'selection'
/// startNode or selection is null
/// selection is of the wrong type
public override IList
GenerateLocatorParts(Object selection, DependencyObject startNode)
{
if (startNode == null)
throw new ArgumentNullException("startNode");
if (selection == null)
throw new ArgumentNullException("selection");
ITextPointer start;
ITextPointer end;
IList textSegments = null;
TextSelectionHelper.CheckSelection(selection, out start, out end, out textSegments);
if (!(start is TextPointer))
throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection");
ITextPointer elementStart;
ITextPointer elementEnd;
// If we can't get the start/end of the node then we can't generate a locator part
if (!GetNodesStartAndEnd(startNode, out elementStart, out elementEnd))
return null;
if (elementStart.CompareTo(end) > 0)
throw new ArgumentException(SR.Get(SRID.InvalidStartNodeForTextSelection), "startNode");
if (elementEnd.CompareTo(start) < 0)
throw new ArgumentException(SR.Get(SRID.InvalidStartNodeForTextSelection), "startNode");
ContentLocatorPart part = new ContentLocatorPart(CharacterRangeElementName);
int startOffset = 0;
int endOffset = 0;
for(int i = 0; i < textSegments.Count; i++)
{
GetTextSegmentValues(textSegments[i], elementStart, elementEnd, out startOffset, out endOffset);
part.NameValuePairs.Add(SegmentAttribute + i.ToString(NumberFormatInfo.InvariantInfo), startOffset.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + endOffset.ToString(NumberFormatInfo.InvariantInfo));
}
part.NameValuePairs.Add(CountAttribute, textSegments.Count.ToString(NumberFormatInfo.InvariantInfo));
List res = new List(1);
res.Add(part);
return res;
}
///
/// Creates a selection object spanning the portion of 'startNode'
/// specified by 'locatorPart'.
///
/// locator part specifying data to be spanned
/// the node to be spanned by the created
/// selection
/// set to AttachmentLevel.Full if the entire range of text
/// was resolved, otherwise set to StartPortion, MiddlePortion, or EndPortion based on
/// which part of the range was resolved
/// a selection spanning the portion of 'startNode' specified by
/// 'locatorPart', null if selection described by locator part could not be
/// recreated
/// locatorPart or startNode are
/// null
/// locatorPart is of the incorrect type
public override Object ResolveLocatorPart(ContentLocatorPart locatorPart, DependencyObject startNode, out AttachmentLevel attachmentLevel)
{
if (startNode == null)
throw new ArgumentNullException("startNode");
if (locatorPart == null)
throw new ArgumentNullException("locatorPart");
if (CharacterRangeElementName != locatorPart.PartType)
throw new ArgumentException(SR.Get(SRID.IncorrectLocatorPartType, locatorPart.PartType.Namespace + ":" + locatorPart.PartType.Name), "locatorPart");
// First we extract the offset and length of the
// text range from the locator part.
int startOffset = 0;
int endOffset = 0;
string stringCount = locatorPart.NameValuePairs[CountAttribute];
if (stringCount == null)
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute));
int count = Int32.Parse(stringCount,NumberFormatInfo.InvariantInfo);
TextAnchor anchor = new TextAnchor();
attachmentLevel = AttachmentLevel.Unresolved;
for (int i = 0; i < count; i++)
{
GetLocatorPartSegmentValues(locatorPart, i, out startOffset, out endOffset);
// Now we grab the TextRange so we can create a selection.
// TextBox doesn't expose its internal TextRange so we use
// its API for creating and getting the selection.
ITextPointer elementStart;
ITextPointer elementEnd;
// If we can't get the start/end of the node then we can't resolve the locator part
if (!GetNodesStartAndEnd(startNode, out elementStart, out elementEnd))
return null;
// If the offset is not withing the element's text range we return null
int textRangeLength = elementStart.GetOffsetToPosition(elementEnd);
if (startOffset > textRangeLength)
return null;
ITextPointer start = elementStart.CreatePointer(startOffset);// new TextPointer((TextPointer)elementStart, startOffset);
ITextPointer end = (textRangeLength <= endOffset) ?
elementEnd.CreatePointer() : //new TextPointer((TextPointer)elementEnd) :
elementStart.CreatePointer(endOffset);// new TextPointer((TextPointer)elementStart, endOffset);
//we do not process 0 length selection
if (start.CompareTo(end) >= 0)
return null;
anchor.AddTextSegment(start, end);
}
//we do not support 0 or negative length selection
if (anchor.IsEmpty)
{
throw new ArgumentException(SR.Get(SRID.IncorrectAnchorLength), "locatorPart");
}
attachmentLevel = AttachmentLevel.Full;
if (_clamping)
{
ITextPointer currentStart = anchor.Start;
ITextPointer currentEnd = anchor.End;
IServiceProvider serviceProvider = null;
ITextView textView = null;
if (_targetPage != null)
{
serviceProvider = _targetPage as IServiceProvider;
}
else
{
FlowDocument content = currentStart.TextContainer.Parent as FlowDocument;
serviceProvider = PathNode.GetParent(content as DependencyObject) as IServiceProvider;
}
Invariant.Assert(serviceProvider != null, "No ServiceProvider found to get TextView from.");
textView = serviceProvider.GetService(typeof(ITextView)) as ITextView;
Invariant.Assert(textView != null, "Null TextView provided by ServiceProvider.");
anchor = TextAnchor.TrimToIntersectionWith(anchor, textView.TextSegments);
if (anchor == null)
{
attachmentLevel = AttachmentLevel.Unresolved;
}
else
{
if (anchor.Start.CompareTo(currentStart) != 0)
{
attachmentLevel &= ~AttachmentLevel.StartPortion;
}
if (anchor.End.CompareTo(currentEnd) != 0)
{
attachmentLevel &= ~AttachmentLevel.EndPortion;
}
}
}
return anchor;
}
///
/// Returns a list of XmlQualifiedNames representing the
/// the locator parts this processor can resolve/generate.
///
public override XmlQualifiedName[] GetLocatorPartTypes()
{
return (XmlQualifiedName[])LocatorPartTypeNames.Clone();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Operators
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Public Events
//
//-----------------------------------------------------
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
///
/// Controls whether or not resolving should clamp the text
/// anchors to the visible portion of a text container. Default
/// value is true.
///
internal bool Clamping
{
set
{
_clamping = value;
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
///
/// Gets the smallest offset and the largest offset from all the segments defined in the locator part.
///
internal static void GetMaxMinLocatorPartValues(ContentLocatorPart locatorPart, out int startOffset, out int endOffset)
{
if (locatorPart == null)
throw new ArgumentNullException("locatorPart");
string stringCount = locatorPart.NameValuePairs[CountAttribute];
if (stringCount == null)
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute));
int count = Int32.Parse(stringCount,NumberFormatInfo.InvariantInfo);
startOffset = Int32.MaxValue;
endOffset = 0;
int segStart;
int segEnd;
for (int i = 0; i < count; i++)
{
GetLocatorPartSegmentValues(locatorPart, i, out segStart, out segEnd);
if (segStart < startOffset)
startOffset = segStart;
if (segEnd > endOffset)
endOffset = segEnd;
}
}
///
/// Set the DocumentPageView this selection processor should use
/// when clamping text anchors. If this target is not set then
/// the set of DPVs held by the viewer are used.
///
internal void SetTargetDocumentPageView(DocumentPageView target)
{
Debug.Assert(target != null);
_targetPage = target;
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Fields
//
//-----------------------------------------------------
#region Internal Fields
// Name of segment attribute
internal const String SegmentAttribute = "Segment";
// Name of segment attribute
internal const String CountAttribute = "Count";
// Name added to a LocatorPart with value "true" to mean
// the LocatorPart matches for any overlapping LocatorPart
internal const String IncludeOverlaps = "IncludeOverlaps";
// Potential separators for values in segment name/value pairs
internal static readonly Char[] Separator = new Char[] { ',' };
// Name of locator part element
internal static readonly XmlQualifiedName CharacterRangeElementName = new XmlQualifiedName("CharacterRange", AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
#endregion Internal Fields
//------------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#region Private Methods
///
/// Extracts the values of attributes from a locator part.
///
/// the locator part to extract values from
/// the number of the segment to extract values for
/// value of offset attribute
/// value of length attribute
private static void GetLocatorPartSegmentValues(ContentLocatorPart locatorPart, int segmentNumber, out int startOffset, out int endOffset)
{
if (segmentNumber < 0)
throw new ArgumentException("segmentNumber");
string segmentString = locatorPart.NameValuePairs[SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)];
string[] values = segmentString.Split(Separator);
if (values.Length != 2)
{
throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
}
startOffset = Int32.Parse(values[0], NumberFormatInfo.InvariantInfo);
endOffset = Int32.Parse(values[1], NumberFormatInfo.InvariantInfo);
}
///
/// Returns the TextContainer for a node that contains text. If the node
/// doesn't have a TextContainer this method returns null.
///
private ITextContainer GetTextContainer(DependencyObject startNode)
{
Debug.Assert(startNode != null);
ITextContainer textContainer = null;
IServiceProvider serviceProvider = startNode as IServiceProvider;
if (serviceProvider != null)
{
textContainer = serviceProvider.GetService(typeof(ITextContainer)) as ITextContainer;
}
if (textContainer == null)
{
// Special case for TextBox which doesn't implement IServiceProvider
TextBoxBase textBox = startNode as TextBoxBase;
if (textBox != null)
{
textContainer = textBox.TextContainer;
}
}
return textContainer;
}
///
/// Returns ITextPointers positioned at the start and end of an element
/// that contains text.
///
private bool GetNodesStartAndEnd(DependencyObject startNode, out ITextPointer start, out ITextPointer end)
{
start = null;
end = null;
ITextContainer textContainer = GetTextContainer(startNode);
if (textContainer != null)
{
start = textContainer.Start;
end = textContainer.End;
}
else
{
// Special case for TextElement which doesn't expose its TextContainer
TextElement textElement = startNode as TextElement;
if (textElement != null)
{
start = textElement.ContentStart;
end = textElement.ContentEnd;
}
else
{
return false;
}
}
return true;
}
///
/// 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.
///
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);
}
}
#endregion Private Methods
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
#region Private Fields
// ContentLocatorPart types understood by this processor
private static readonly XmlQualifiedName[] LocatorPartTypeNames =
new XmlQualifiedName[]
{
CharacterRangeElementName
};
// Optional DPV - used in printing case when there is no viewer available
private DocumentPageView _targetPage = null;
// Controls whether or not resolving clamps text anchors to
// the visible portion of a TextContainer
private bool _clamping = true;
#endregion Private Fields
}
}
// 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
- wmiprovider.cs
- XsltLibrary.cs
- CodeAttributeArgumentCollection.cs
- SqlDataReader.cs
- SvcFileManager.cs
- ControlOperationBehavior.cs
- PropertyConverter.cs
- PeerResolverSettings.cs
- SqlTypesSchemaImporter.cs
- SerialReceived.cs
- SecurityKeyIdentifierClause.cs
- DebugView.cs
- SafePointer.cs
- UiaCoreProviderApi.cs
- Evaluator.cs
- HttpValueCollection.cs
- BamlMapTable.cs
- OleDbRowUpdatedEvent.cs
- AssertFilter.cs
- BamlLocalizerErrorNotifyEventArgs.cs
- SocketAddress.cs
- TemplateBindingExpression.cs
- DetailsViewRowCollection.cs
- XmlDataSourceDesigner.cs
- SqlBuilder.cs
- BooleanProjectedSlot.cs
- SchemaCollectionPreprocessor.cs
- OdbcStatementHandle.cs
- NavigationEventArgs.cs
- TextDecoration.cs
- InputScope.cs
- GeometryConverter.cs
- ResourcePool.cs
- ManagedCodeMarkers.cs
- XmlHelper.cs
- Certificate.cs
- ServiceNameElement.cs
- SecurityElement.cs
- DynamicRenderer.cs
- ExceptionHelpers.cs
- MessagePropertyDescriptionCollection.cs
- AutomationPatternInfo.cs
- StoreContentChangedEventArgs.cs
- MdiWindowListStrip.cs
- RepeatBehavior.cs
- WebConvert.cs
- ManipulationBoundaryFeedbackEventArgs.cs
- AlphabetConverter.cs
- IsolatedStorage.cs
- XmlTextEncoder.cs
- ImageInfo.cs
- Int32Storage.cs
- AggregateNode.cs
- DirectoryObjectSecurity.cs
- Int32Animation.cs
- EventWaitHandle.cs
- TimeSpanSecondsConverter.cs
- dataobject.cs
- EventsTab.cs
- BindingMAnagerBase.cs
- FontEmbeddingManager.cs
- FileNameEditor.cs
- PackageRelationship.cs
- ProgressBar.cs
- TimeSpanConverter.cs
- FormViewInsertEventArgs.cs
- MessageAction.cs
- StringResourceManager.cs
- DbCommandDefinition.cs
- HtmlInputSubmit.cs
- CodeParameterDeclarationExpression.cs
- CanonicalXml.cs
- StateRuntime.cs
- DuplicateWaitObjectException.cs
- SingleKeyFrameCollection.cs
- AQNBuilder.cs
- UInt64Converter.cs
- BinarySecretSecurityToken.cs
- AssemblyEvidenceFactory.cs
- MediaSystem.cs
- HyperLinkField.cs
- RuleAction.cs
- PerformanceCounter.cs
- ImageAnimator.cs
- InputMethod.cs
- X509Certificate.cs
- Metadata.cs
- TextFormatterImp.cs
- WeakRefEnumerator.cs
- ProxyManager.cs
- SecureStringHasher.cs
- KnownBoxes.cs
- SerTrace.cs
- ValidationSummary.cs
- ComponentDispatcherThread.cs
- DefaultWorkflowLoaderService.cs
- BamlVersionHeader.cs
- WebPartConnectionsConfigureVerb.cs
- PointCollectionConverter.cs
- RegexBoyerMoore.cs