TextSelectionProcessor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK