FixedTextSelectionProcessor.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 / FixedTextSelectionProcessor.cs / 1 / FixedTextSelectionProcessor.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//     FixedTextSelectionProcessor uses TextAnchors to represent portions 
//     of text that are anchors. It produces FixedTextRange locator parts that are designed
//     specifically for use inside the DocumentViewer control. This locator part contains the 
//     page number and the start and end points of the text selection.
//     FixedTextSelectionProcessor converts the text selection to FixedTextRange
//	
//     Spec: http://team/sites/ag/Specifications/Anchoring%20to%20text%20in%20paginated%20docs.doc 
//
// History: 
//  09/29/2004: ssimova: Created 
//  12/07/2004: rruiz:   Updated processor to work on new DocumentViewer
//----------------------------------------------------------------------------- 
using System;
using System.IO;
using System.Windows;
using System.Collections; 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Diagnostics; 
using System.Globalization;
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;
using MS.Internal.PtsHost; 

namespace MS.Internal.Annotations.Anchoring
{
    ///  
    ///     FixedTextSelectionProcessor uses TextAnchors to represent portions
    ///     of text that are anchors.  It produces locator parts that 
    ///     represent these TextRanges by beginning and end position 
    /// 
    internal class FixedTextSelectionProcessor : SelectionProcessor 
    {
        //-----------------------------------------------------
        //
        //  Constructors 
        //
        //----------------------------------------------------- 
 
        #region Constructors
 
        /// 
        ///     Creates an instance of FixedTextSelectionProcessor.
        /// 
        public FixedTextSelectionProcessor() 
        {
        } 
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
 
        #region Public Methods 

        ///  
        /// Merges the two anchors into one, if possible. It does not require
        /// the anchors to be connected. All this method does is to create a
        /// TextAnchor that spans the two anchors.
        ///  
        /// anchor to merge. Must be a TextAnchor. 
        /// other anchor to merge. Must be a TextAnchor.  
        /// new anchor that contains the data from both 
        /// anchor1 and anchor2
        /// true if the anchors were merged, false otherwise 
        /// 
        public override bool MergeSelections(Object anchor1, Object anchor2, out Object newAnchor)
        {
            return TextSelectionHelper.MergeSelections(anchor1, anchor2, out newAnchor); 
        }
 
 
        /// 
        ///  Generates FixedPageProxy objects for each page, spaned by the selection 
        /// 
        /// the selection to examine. Must implement ITextRange
        /// a list of FixedPageProxy objects, corresponding to each page spanned by the selection; never returns
        /// null 
        /// selection is null
        /// selection is of wrong type 
        /// selection start or end point can not be resolved to a page 
        public override IList GetSelectedNodes(Object selection)
        { 
            IList textSegments = CheckSelection(selection);

            IList pageEl = new List();
 
            Point start;
            Point end; 
            foreach (TextSegment segment in textSegments) 
            {
                int startPage = int.MinValue; 
                ITextPointer startPointer = segment.Start.CreatePointer(LogicalDirection.Forward);
                TextSelectionHelper.GetPointerPage(startPointer, out startPage);
                start = TextSelectionHelper.GetPointForPointer(startPointer);
                if (startPage == int.MinValue) 
                    throw new ArgumentException(SR.Get(SRID.SelectionDoesNotResolveToAPage, "start"), "selection");
 
                int endPage = int.MinValue; 
                ITextPointer endPointer = segment.End.CreatePointer(LogicalDirection.Backward);
                TextSelectionHelper.GetPointerPage(endPointer, out endPage); 
                end = TextSelectionHelper.GetPointForPointer(endPointer);
                if (endPage == int.MinValue)
                    throw new ArgumentException(SR.Get(SRID.SelectionDoesNotResolveToAPage, "end"), "selection");
 
                int firstPage = pageEl.Count;
                int numOfPages = endPage - startPage; 
 
                Debug.Assert(numOfPages >= 0, "start page number is bigger than the end page number");
 
                // If the first page of this segment already has an FPP, then use that one for an additional segment
                int i = 0;

                if (pageEl.Count > 0 && ((FixedPageProxy)pageEl[pageEl.Count - 1]).Page == startPage) 
                {
                    firstPage--;  // use the existing one from the list as the first 
                    i++;  // make 1 fewer FPPs 

                } 

                for (; i <= numOfPages; i++)
                {
                    pageEl.Add(new FixedPageProxy(segment.Start.TextContainer.Parent, startPage + i)); 
                }
 
                // If entire segment is on one page set both start/end on that page 
                if (numOfPages == 0)
                { 
                    ((FixedPageProxy)pageEl[firstPage]).Segments.Add(new PointSegment(start, end));
                }
                else
                { 
                    // otherwise set start on the first page and end on the last page
                    ((FixedPageProxy)pageEl[firstPage]).Segments.Add(new PointSegment(start, PointSegment.NotAPoint)); 
                    ((FixedPageProxy)pageEl[firstPage + numOfPages]).Segments.Add(new PointSegment(PointSegment.NotAPoint, end)); 
                }
            } 

            return pageEl;
        }
 
        /// 
        /// Gets the parent element of this selection. The parent element is the 
        /// FixedPage that contains selection.Start TextPointer. 
        /// 
        /// the selection to examine. Must implement ITextRange 
        /// the parent element of the selection; can be null
        /// selection is null
        /// selection is of wrong type
        public override UIElement GetParent(Object selection) 
        {
            CheckAnchor(selection); 
            return TextSelectionHelper.GetParent(selection); 
        }
 

        /// 
        /// Gets the anchor point for the selection. This is the Point that corresponds
        /// to the start position of the selection 
        /// 
        /// the selection to examine. Must implement ITextRange 
        /// the anchor point of the selection; can be (double.NaN, double.NaN) if the 
        /// selection start point is not contained in a document viewer
        /// selection is null 
        /// selection is of wrong type
        public override Point GetAnchorPoint(Object selection)
        {
            CheckAnchor(selection); 
            return TextSelectionHelper.GetAnchorPoint(selection);
        } 
 
        /// 
        ///     Creates one locator part representing part of the selection 
        /// that lies within start node
        /// 
        /// the selection that is being processed. Must implement ITextRange
        /// The FixedPageProxy object, representing one page of the document 
        /// A list containing one FixedTextRange locator part
        /// 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"); 
 
            CheckSelection(selection);
 
            FixedPageProxy fp = startNode as FixedPageProxy;

            if (fp == null)
                throw new ArgumentException(SR.Get(SRID.StartNodeMustBeFixedPageProxy), "startNode"); 

            ContentLocatorPart part = new ContentLocatorPart(FixedTextElementName); 
            if (fp.Segments.Count == 0) 
            {
                part.NameValuePairs.Add(TextSelectionProcessor.CountAttribute, 1.ToString(NumberFormatInfo.InvariantInfo)); 
                part.NameValuePairs.Add(TextSelectionProcessor.SegmentAttribute + 0.ToString(NumberFormatInfo.InvariantInfo), ",,,");
            }
            else
            { 
                part.NameValuePairs.Add(TextSelectionProcessor.CountAttribute, fp.Segments.Count.ToString(NumberFormatInfo.InvariantInfo));
 
                for (int i = 0; i < fp.Segments.Count; i++) 
                {
                    string value = ""; 
                    if (!double.IsNaN(fp.Segments[i].Start.X))
                    {
                        value += fp.Segments[i].Start.X.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + fp.Segments[i].Start.Y.ToString(NumberFormatInfo.InvariantInfo);
                    } 
                    else
                    { 
                        value += TextSelectionProcessor.Separator[0]; 
                    }
                    value += TextSelectionProcessor.Separator[0]; 
                    if (!double.IsNaN(fp.Segments[i].End.X))
                    {
                        value += fp.Segments[i].End.X.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + fp.Segments[i].End.Y.ToString(NumberFormatInfo.InvariantInfo);
                    } 
                    else
                    { 
                        value += TextSelectionProcessor.Separator[0]; 
                    }
 
                    part.NameValuePairs.Add(TextSelectionProcessor.SegmentAttribute + i.ToString(NumberFormatInfo.InvariantInfo), value);
                }
            }
            List res = new List(1); 
            res.Add(part);
            return res; 
 
        }
 
        /// 
        ///     Creates a TextRange object spanning the portion of 'startNode'
        ///     specified by 'locatorPart'.
        ///  
        /// FixedTextRange locator part specifying start and end point of
        /// the TextRange 
        /// the FixedPage containing this locator part 
        /// set to AttachmentLevel.Full if the FixedPage for the locator
        /// part was found, AttachmentLevel.Unresolved otherwise 
        /// a TextRange spanning the text between start end end point in the FixedTextRange
        /// locator part
        /// , null if selection described by locator part could not be
        /// recreated 
        /// locatorPart or startNode are
        /// null 
        /// locatorPart is of the incorrect type 
        /// startNode is not a FixedPage
        /// startNode does not belong to the DocumentViewer 
        public override Object ResolveLocatorPart(ContentLocatorPart locatorPart, DependencyObject startNode, out AttachmentLevel attachmentLevel)
        {
            if (startNode == null)
                throw new ArgumentNullException("startNode"); 

            DocumentPage docPage = null; 
            FixedPage page = startNode as FixedPage; 

            if (page != null) 
            {
                docPage = GetDocumentPage(page);
            }
            else 
            {
                // If we were passed a DPV because we are walking the visual tree, 
                // extract the DocumentPage from it;  its TextView will be used to 
                // turn coordinates into text positions
                DocumentPageView dpv = startNode as DocumentPageView; 
                if (dpv != null)
                {
                    docPage = dpv.DocumentPage as FixedDocumentPage;
                    if (docPage == null) 
                    {
                        docPage = dpv.DocumentPage as FixedDocumentSequenceDocumentPage; 
                    } 
                }
            } 

            if (docPage == null)
            {
                throw new ArgumentException(SR.Get(SRID.StartNodeMustBeDocumentPageViewOrFixedPage), "startNode"); 
            }
 
            if (locatorPart == null) 
                throw new ArgumentNullException("locatorPart");
 
            attachmentLevel = AttachmentLevel.Unresolved;

            ITextView tv = (ITextView)((IServiceProvider)docPage).GetService(typeof(ITextView));
            Debug.Assert(tv != null); 

            ReadOnlyCollection ts = tv.TextSegments; 
 
            //check first if a TextRange can be generated
            if (ts == null || ts.Count <= 0) 
                return null;

            TextAnchor resolvedAnchor = new TextAnchor();
 
            if (docPage != null)
            { 
                string stringCount = locatorPart.NameValuePairs["Count"]; 
                if (stringCount == null)
                    throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute)); 
                int count = Int32.Parse(stringCount, NumberFormatInfo.InvariantInfo);

                for(int i = 0; i < count; i++)
                { 
                    // First we extract the start and end Point from the locator part.
                    Point start; 
                    Point end; 
                    GetLocatorPartSegmentValues(locatorPart, i, out start, out end);
 
                    //calulate start ITextPointer
                    ITextPointer segStart;
                    if (double.IsNaN(start.X) || double.IsNaN(start.Y))
                    { 
                        //get start of the page
                        segStart = FindStartVisibleTextPointer(docPage); 
                    } 
                    else
                    { 
                        //convert Point to TextPointer
                        segStart = tv.GetTextPositionFromPoint(start, true);
                    }
 
                    if (segStart == null)
                    { 
                        //selStart can be null if there are no insertion points on this page 
                        continue;
                    } 

                    //calulate end ITextPointer
                    ITextPointer segEnd;
                    if (double.IsNaN(end.X) || double.IsNaN(end.Y)) 
                    {
                        segEnd = FindEndVisibleTextPointer(docPage); 
                    } 
                    else
                    { 
                        //convert Point to TextPointer
                        segEnd = tv.GetTextPositionFromPoint(end, true);
                    }
 
                    //end TP can not be null when start is not
                    Invariant.Assert(segEnd != null, "end TP is null when start TP is not"); 
 
                    attachmentLevel = AttachmentLevel.Full;  // Not always true right?
                    resolvedAnchor.AddTextSegment(segStart, segEnd); 
                }
            }

            if (resolvedAnchor.TextSegments.Count > 0) 
                return resolvedAnchor;
            else 
                return null; 
        }
 
        /// 
        ///     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
        // 
        //-----------------------------------------------------
        //-----------------------------------------------------
        //
        //  Private Methods 
        //
        //------------------------------------------------------ 
 
        #region Private Methods
 
        private DocumentPage GetDocumentPage(FixedPage page)
        {
            Invariant.Assert(page != null);
 
            DocumentPage docPage = null;
            PageContent content = page.Parent as PageContent; 
            if (content != null) 
            {
                FixedDocument document = content.Parent as FixedDocument; 

                // If the document is part of a FixedDocumentSequence then we want to get the
                // FixedDocumentSequenceDocumentPage for the FixedPage (cause its TextView is
                // the one we want to use). 
                FixedDocumentSequence sequence = document.Parent as FixedDocumentSequence;
                if (sequence != null) 
                { 
                    docPage = sequence.GetPage(document, document.GetIndexOfPage(page));
                } 
                else
                {
                    docPage = document.GetPage(document.GetIndexOfPage(page));
                } 
            }
            return docPage; 
        } 

        ///  
        /// Checks if the selection object satisfies the requirements
        /// for this processor
        /// 
        /// selection 
        /// ITextRange interface, implemented by the object
        private IList CheckSelection(object selection) 
        { 
            if (selection == null)
                throw new ArgumentNullException("selection"); 

            IList textSegments = null;
            ITextPointer start = null;
            ITextRange textRange = selection as ITextRange; 

            if (textRange != null) 
            { 
                start = textRange.Start;
                textSegments = textRange.TextSegments; 
            }
            else
            {
                TextAnchor anchor = selection as TextAnchor; 
                if (anchor != null)
                { 
                    start = anchor.Start; 
                    textSegments = anchor.TextSegments;
                } 
                else
                {
                    throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString());
                } 
            }
 
            if (!(start.TextContainer is FixedTextContainer || 
                start.TextContainer is DocumentSequenceTextContainer))
                throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString()); 

            return textSegments;
        }
 
        /// 
        /// Checks if the selection object satisfies the requirements 
        /// for this processor 
        /// 
        /// selection 
        /// ITextRange interface, implemented by the object
        private TextAnchor CheckAnchor(object selection)
        {
            if (selection == null) 
                throw new ArgumentNullException("selection");
 
            TextAnchor anchor = selection as TextAnchor; 

            if (anchor == null || !(anchor.Start.TextContainer is FixedTextContainer || 
                    anchor.Start.TextContainer is DocumentSequenceTextContainer))
            {
                throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString());
            } 

            return anchor; 
        } 

        ///  
        ///     Extracts the values of attributes from a locator part.
        /// 
        /// the locator part to extract values from
        /// number of segment value to retrieve 
        /// the start point value based on StartXAttribute and StartYAttribute values
        /// the end point value based on EndXAttribyte and EndYattribute values 
        private void GetLocatorPartSegmentValues(ContentLocatorPart locatorPart, int segmentNumber, out Point start, out Point end) 
        {
            if (locatorPart == null) 
                throw new ArgumentNullException("locatorPart");

            if (FixedTextElementName != locatorPart.PartType)
                throw new ArgumentException(SR.Get(SRID.IncorrectLocatorPartType, locatorPart.PartType.Namespace + ":" + locatorPart.PartType.Name), "locatorPart"); 

            string segmentValue = locatorPart.NameValuePairs[TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)]; 
            if (segmentValue == null) 
                throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
 
            string[] values = segmentValue.Split(TextSelectionProcessor.Separator);
            if (values.Length != 4)
                throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
            start = GetPoint(values[0], values[1]); 
            end = GetPoint(values[2], values[3]);
        } 
 
        /// 
        /// Calculate Point out of string X and Y values 
        /// 
        /// x string value
        /// y string value
        ///  
        private Point GetPoint(string xstr, string ystr)
        { 
            Point point; 
            if (xstr != null && !String.IsNullOrEmpty(xstr.Trim()) && ystr != null && !String.IsNullOrEmpty(ystr.Trim()))
            { 
                double x = Double.Parse(xstr, NumberFormatInfo.InvariantInfo);
                double y = Double.Parse(ystr, NumberFormatInfo.InvariantInfo);
                point = new Point(x, y);
            } 
            else
            { 
                point = new Point(double.NaN, double.NaN); 
            }
 
            return point;

        }
 
        /// 
        /// Gets the first visible TP on a DocumentPage 
        ///  
        /// document page
        /// The first visible TP or null if no visible TP on this page 
        private static ITextPointer FindStartVisibleTextPointer(DocumentPage documentPage)
        {
            ITextPointer start, end;
            if (!GetTextViewRange(documentPage, out start, out end)) 
                return null;
 
            if (!start.IsAtInsertionPosition && !start.MoveToNextInsertionPosition(LogicalDirection.Forward)) 
            {
                //there is no insertion point in this direction 
                return null;
            }

            //check if it is outside of the page 
            if (start.CompareTo(end) > 0)
                return null; 
 
            return start;
 
        }

        /// 
        /// Gets the last visible TP on a DocumentPage 
        /// 
        /// document page 
        /// The last visible TP or null if no visible TP on the page 
        private static ITextPointer FindEndVisibleTextPointer(DocumentPage documentPage)
        { 
            ITextPointer start, end;
            if (!GetTextViewRange(documentPage, out start, out end))
                return null;
 
            if (!end.IsAtInsertionPosition && !end.MoveToNextInsertionPosition(LogicalDirection.Backward))
            { 
                //there is no insertion point in this direction 
                return null;
            } 
            //check if it is outside of the page
            if (start.CompareTo(end) > 0)
                return null;
 
            return end;
 
        } 

        ///  
        /// Gets first and last TP on a documentPage.
        /// 
        /// the document page
        /// start TP 
        /// end TP
        /// true if there aretext segments on this page, otherwise false 
        private static bool GetTextViewRange(DocumentPage documentPage, out ITextPointer start, out ITextPointer end) 
        {
            ITextView textView; 
            start = end = null;

            // Missing pages can't produce TextPointers
            Invariant.Assert(documentPage != DocumentPage.Missing); 

            textView = ((IServiceProvider)documentPage).GetService(typeof(ITextView)) as ITextView; 
            Invariant.Assert(textView != null, "DocumentPage didn't provide a TextView."); 

            //check if there is any content 
            if ((textView.TextSegments == null) || (textView.TextSegments.Count == 0))
                return false;

            start = textView.TextSegments[0].Start.CreatePointer(LogicalDirection.Forward); 
            end = textView.TextSegments[textView.TextSegments.Count - 1].End.CreatePointer(LogicalDirection.Backward);
            Debug.Assert((start != null) && (end != null), "null start/end TextPointer on a non empty page"); 
            return true; 
        }
 
        #endregion Private Methods

        //-----------------------------------------------------
        // 
        //  Private Fields
        // 
        //------------------------------------------------------ 

        #region Private Fields 

        // Name of locator part element
        private static readonly XmlQualifiedName FixedTextElementName = new XmlQualifiedName("FixedTextRange", AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
 
        // ContentLocatorPart types understood by this processor
        private static readonly XmlQualifiedName[] LocatorPartTypeNames = 
                new XmlQualifiedName[] 
                {
                    FixedTextElementName 
                };

        #endregion Private Fields
 
        #region Internal Classes
        ///  
        /// Returned by GetSelectedNodes - one object per page spanned by the selection 
        /// 
        internal sealed class FixedPageProxy : DependencyObject 
        {
            public FixedPageProxy(DependencyObject parent, int page)
            {
                SetValue(PathNode.HiddenParentProperty, parent); 
                _page = page;
            } 
 
            public int Page
            { 
                get
                {
                    return _page;
                } 
            }
 
            public IList Segments 
            {
                get 
                {
                    return _segments;
                }
            } 

 
            int _page; 
            IList _segments = new List(1);
        } 

        /// 
        /// PointSegment represents a segment in fixed content with start and end points.
        ///  
        internal sealed class PointSegment
        { 
            ///  
            /// Creates a PointSegment with the given points.
            ///  
            internal PointSegment(Point start, Point end)
            {
                _start = start;
                _end = end; 
            }
 
            ///  
            /// The start point of the segment
            ///  
            public Point Start
            {
                get
                { 
                    return _start;
                } 
            } 

            ///  
            /// The end point of the segment
            /// 
            public Point End
            { 
                get
                { 
                    return _end; 
                }
            } 

            /// 
            /// Used to represent a non-existent point - for instance for a segment
            /// which spans an entire page there is no starting point. 
            /// 
            public static readonly Point NotAPoint = new Point(double.NaN, double.NaN); 
 
            private Point _start;
            private Point _end; 
        }

        #endregion Internal Classes
 
    }
 
} 

// 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: 
//     FixedTextSelectionProcessor uses TextAnchors to represent portions 
//     of text that are anchors. It produces FixedTextRange locator parts that are designed
//     specifically for use inside the DocumentViewer control. This locator part contains the 
//     page number and the start and end points of the text selection.
//     FixedTextSelectionProcessor converts the text selection to FixedTextRange
//	
//     Spec: http://team/sites/ag/Specifications/Anchoring%20to%20text%20in%20paginated%20docs.doc 
//
// History: 
//  09/29/2004: ssimova: Created 
//  12/07/2004: rruiz:   Updated processor to work on new DocumentViewer
//----------------------------------------------------------------------------- 
using System;
using System.IO;
using System.Windows;
using System.Collections; 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Diagnostics; 
using System.Globalization;
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;
using MS.Internal.PtsHost; 

namespace MS.Internal.Annotations.Anchoring
{
    ///  
    ///     FixedTextSelectionProcessor uses TextAnchors to represent portions
    ///     of text that are anchors.  It produces locator parts that 
    ///     represent these TextRanges by beginning and end position 
    /// 
    internal class FixedTextSelectionProcessor : SelectionProcessor 
    {
        //-----------------------------------------------------
        //
        //  Constructors 
        //
        //----------------------------------------------------- 
 
        #region Constructors
 
        /// 
        ///     Creates an instance of FixedTextSelectionProcessor.
        /// 
        public FixedTextSelectionProcessor() 
        {
        } 
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        // 
        //-----------------------------------------------------
 
        #region Public Methods 

        ///  
        /// Merges the two anchors into one, if possible. It does not require
        /// the anchors to be connected. All this method does is to create a
        /// TextAnchor that spans the two anchors.
        ///  
        /// anchor to merge. Must be a TextAnchor. 
        /// other anchor to merge. Must be a TextAnchor.  
        /// new anchor that contains the data from both 
        /// anchor1 and anchor2
        /// true if the anchors were merged, false otherwise 
        /// 
        public override bool MergeSelections(Object anchor1, Object anchor2, out Object newAnchor)
        {
            return TextSelectionHelper.MergeSelections(anchor1, anchor2, out newAnchor); 
        }
 
 
        /// 
        ///  Generates FixedPageProxy objects for each page, spaned by the selection 
        /// 
        /// the selection to examine. Must implement ITextRange
        /// a list of FixedPageProxy objects, corresponding to each page spanned by the selection; never returns
        /// null 
        /// selection is null
        /// selection is of wrong type 
        /// selection start or end point can not be resolved to a page 
        public override IList GetSelectedNodes(Object selection)
        { 
            IList textSegments = CheckSelection(selection);

            IList pageEl = new List();
 
            Point start;
            Point end; 
            foreach (TextSegment segment in textSegments) 
            {
                int startPage = int.MinValue; 
                ITextPointer startPointer = segment.Start.CreatePointer(LogicalDirection.Forward);
                TextSelectionHelper.GetPointerPage(startPointer, out startPage);
                start = TextSelectionHelper.GetPointForPointer(startPointer);
                if (startPage == int.MinValue) 
                    throw new ArgumentException(SR.Get(SRID.SelectionDoesNotResolveToAPage, "start"), "selection");
 
                int endPage = int.MinValue; 
                ITextPointer endPointer = segment.End.CreatePointer(LogicalDirection.Backward);
                TextSelectionHelper.GetPointerPage(endPointer, out endPage); 
                end = TextSelectionHelper.GetPointForPointer(endPointer);
                if (endPage == int.MinValue)
                    throw new ArgumentException(SR.Get(SRID.SelectionDoesNotResolveToAPage, "end"), "selection");
 
                int firstPage = pageEl.Count;
                int numOfPages = endPage - startPage; 
 
                Debug.Assert(numOfPages >= 0, "start page number is bigger than the end page number");
 
                // If the first page of this segment already has an FPP, then use that one for an additional segment
                int i = 0;

                if (pageEl.Count > 0 && ((FixedPageProxy)pageEl[pageEl.Count - 1]).Page == startPage) 
                {
                    firstPage--;  // use the existing one from the list as the first 
                    i++;  // make 1 fewer FPPs 

                } 

                for (; i <= numOfPages; i++)
                {
                    pageEl.Add(new FixedPageProxy(segment.Start.TextContainer.Parent, startPage + i)); 
                }
 
                // If entire segment is on one page set both start/end on that page 
                if (numOfPages == 0)
                { 
                    ((FixedPageProxy)pageEl[firstPage]).Segments.Add(new PointSegment(start, end));
                }
                else
                { 
                    // otherwise set start on the first page and end on the last page
                    ((FixedPageProxy)pageEl[firstPage]).Segments.Add(new PointSegment(start, PointSegment.NotAPoint)); 
                    ((FixedPageProxy)pageEl[firstPage + numOfPages]).Segments.Add(new PointSegment(PointSegment.NotAPoint, end)); 
                }
            } 

            return pageEl;
        }
 
        /// 
        /// Gets the parent element of this selection. The parent element is the 
        /// FixedPage that contains selection.Start TextPointer. 
        /// 
        /// the selection to examine. Must implement ITextRange 
        /// the parent element of the selection; can be null
        /// selection is null
        /// selection is of wrong type
        public override UIElement GetParent(Object selection) 
        {
            CheckAnchor(selection); 
            return TextSelectionHelper.GetParent(selection); 
        }
 

        /// 
        /// Gets the anchor point for the selection. This is the Point that corresponds
        /// to the start position of the selection 
        /// 
        /// the selection to examine. Must implement ITextRange 
        /// the anchor point of the selection; can be (double.NaN, double.NaN) if the 
        /// selection start point is not contained in a document viewer
        /// selection is null 
        /// selection is of wrong type
        public override Point GetAnchorPoint(Object selection)
        {
            CheckAnchor(selection); 
            return TextSelectionHelper.GetAnchorPoint(selection);
        } 
 
        /// 
        ///     Creates one locator part representing part of the selection 
        /// that lies within start node
        /// 
        /// the selection that is being processed. Must implement ITextRange
        /// The FixedPageProxy object, representing one page of the document 
        /// A list containing one FixedTextRange locator part
        /// 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"); 
 
            CheckSelection(selection);
 
            FixedPageProxy fp = startNode as FixedPageProxy;

            if (fp == null)
                throw new ArgumentException(SR.Get(SRID.StartNodeMustBeFixedPageProxy), "startNode"); 

            ContentLocatorPart part = new ContentLocatorPart(FixedTextElementName); 
            if (fp.Segments.Count == 0) 
            {
                part.NameValuePairs.Add(TextSelectionProcessor.CountAttribute, 1.ToString(NumberFormatInfo.InvariantInfo)); 
                part.NameValuePairs.Add(TextSelectionProcessor.SegmentAttribute + 0.ToString(NumberFormatInfo.InvariantInfo), ",,,");
            }
            else
            { 
                part.NameValuePairs.Add(TextSelectionProcessor.CountAttribute, fp.Segments.Count.ToString(NumberFormatInfo.InvariantInfo));
 
                for (int i = 0; i < fp.Segments.Count; i++) 
                {
                    string value = ""; 
                    if (!double.IsNaN(fp.Segments[i].Start.X))
                    {
                        value += fp.Segments[i].Start.X.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + fp.Segments[i].Start.Y.ToString(NumberFormatInfo.InvariantInfo);
                    } 
                    else
                    { 
                        value += TextSelectionProcessor.Separator[0]; 
                    }
                    value += TextSelectionProcessor.Separator[0]; 
                    if (!double.IsNaN(fp.Segments[i].End.X))
                    {
                        value += fp.Segments[i].End.X.ToString(NumberFormatInfo.InvariantInfo) + TextSelectionProcessor.Separator[0] + fp.Segments[i].End.Y.ToString(NumberFormatInfo.InvariantInfo);
                    } 
                    else
                    { 
                        value += TextSelectionProcessor.Separator[0]; 
                    }
 
                    part.NameValuePairs.Add(TextSelectionProcessor.SegmentAttribute + i.ToString(NumberFormatInfo.InvariantInfo), value);
                }
            }
            List res = new List(1); 
            res.Add(part);
            return res; 
 
        }
 
        /// 
        ///     Creates a TextRange object spanning the portion of 'startNode'
        ///     specified by 'locatorPart'.
        ///  
        /// FixedTextRange locator part specifying start and end point of
        /// the TextRange 
        /// the FixedPage containing this locator part 
        /// set to AttachmentLevel.Full if the FixedPage for the locator
        /// part was found, AttachmentLevel.Unresolved otherwise 
        /// a TextRange spanning the text between start end end point in the FixedTextRange
        /// locator part
        /// , null if selection described by locator part could not be
        /// recreated 
        /// locatorPart or startNode are
        /// null 
        /// locatorPart is of the incorrect type 
        /// startNode is not a FixedPage
        /// startNode does not belong to the DocumentViewer 
        public override Object ResolveLocatorPart(ContentLocatorPart locatorPart, DependencyObject startNode, out AttachmentLevel attachmentLevel)
        {
            if (startNode == null)
                throw new ArgumentNullException("startNode"); 

            DocumentPage docPage = null; 
            FixedPage page = startNode as FixedPage; 

            if (page != null) 
            {
                docPage = GetDocumentPage(page);
            }
            else 
            {
                // If we were passed a DPV because we are walking the visual tree, 
                // extract the DocumentPage from it;  its TextView will be used to 
                // turn coordinates into text positions
                DocumentPageView dpv = startNode as DocumentPageView; 
                if (dpv != null)
                {
                    docPage = dpv.DocumentPage as FixedDocumentPage;
                    if (docPage == null) 
                    {
                        docPage = dpv.DocumentPage as FixedDocumentSequenceDocumentPage; 
                    } 
                }
            } 

            if (docPage == null)
            {
                throw new ArgumentException(SR.Get(SRID.StartNodeMustBeDocumentPageViewOrFixedPage), "startNode"); 
            }
 
            if (locatorPart == null) 
                throw new ArgumentNullException("locatorPart");
 
            attachmentLevel = AttachmentLevel.Unresolved;

            ITextView tv = (ITextView)((IServiceProvider)docPage).GetService(typeof(ITextView));
            Debug.Assert(tv != null); 

            ReadOnlyCollection ts = tv.TextSegments; 
 
            //check first if a TextRange can be generated
            if (ts == null || ts.Count <= 0) 
                return null;

            TextAnchor resolvedAnchor = new TextAnchor();
 
            if (docPage != null)
            { 
                string stringCount = locatorPart.NameValuePairs["Count"]; 
                if (stringCount == null)
                    throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.CountAttribute)); 
                int count = Int32.Parse(stringCount, NumberFormatInfo.InvariantInfo);

                for(int i = 0; i < count; i++)
                { 
                    // First we extract the start and end Point from the locator part.
                    Point start; 
                    Point end; 
                    GetLocatorPartSegmentValues(locatorPart, i, out start, out end);
 
                    //calulate start ITextPointer
                    ITextPointer segStart;
                    if (double.IsNaN(start.X) || double.IsNaN(start.Y))
                    { 
                        //get start of the page
                        segStart = FindStartVisibleTextPointer(docPage); 
                    } 
                    else
                    { 
                        //convert Point to TextPointer
                        segStart = tv.GetTextPositionFromPoint(start, true);
                    }
 
                    if (segStart == null)
                    { 
                        //selStart can be null if there are no insertion points on this page 
                        continue;
                    } 

                    //calulate end ITextPointer
                    ITextPointer segEnd;
                    if (double.IsNaN(end.X) || double.IsNaN(end.Y)) 
                    {
                        segEnd = FindEndVisibleTextPointer(docPage); 
                    } 
                    else
                    { 
                        //convert Point to TextPointer
                        segEnd = tv.GetTextPositionFromPoint(end, true);
                    }
 
                    //end TP can not be null when start is not
                    Invariant.Assert(segEnd != null, "end TP is null when start TP is not"); 
 
                    attachmentLevel = AttachmentLevel.Full;  // Not always true right?
                    resolvedAnchor.AddTextSegment(segStart, segEnd); 
                }
            }

            if (resolvedAnchor.TextSegments.Count > 0) 
                return resolvedAnchor;
            else 
                return null; 
        }
 
        /// 
        ///     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
        // 
        //-----------------------------------------------------
        //-----------------------------------------------------
        //
        //  Private Methods 
        //
        //------------------------------------------------------ 
 
        #region Private Methods
 
        private DocumentPage GetDocumentPage(FixedPage page)
        {
            Invariant.Assert(page != null);
 
            DocumentPage docPage = null;
            PageContent content = page.Parent as PageContent; 
            if (content != null) 
            {
                FixedDocument document = content.Parent as FixedDocument; 

                // If the document is part of a FixedDocumentSequence then we want to get the
                // FixedDocumentSequenceDocumentPage for the FixedPage (cause its TextView is
                // the one we want to use). 
                FixedDocumentSequence sequence = document.Parent as FixedDocumentSequence;
                if (sequence != null) 
                { 
                    docPage = sequence.GetPage(document, document.GetIndexOfPage(page));
                } 
                else
                {
                    docPage = document.GetPage(document.GetIndexOfPage(page));
                } 
            }
            return docPage; 
        } 

        ///  
        /// Checks if the selection object satisfies the requirements
        /// for this processor
        /// 
        /// selection 
        /// ITextRange interface, implemented by the object
        private IList CheckSelection(object selection) 
        { 
            if (selection == null)
                throw new ArgumentNullException("selection"); 

            IList textSegments = null;
            ITextPointer start = null;
            ITextRange textRange = selection as ITextRange; 

            if (textRange != null) 
            { 
                start = textRange.Start;
                textSegments = textRange.TextSegments; 
            }
            else
            {
                TextAnchor anchor = selection as TextAnchor; 
                if (anchor != null)
                { 
                    start = anchor.Start; 
                    textSegments = anchor.TextSegments;
                } 
                else
                {
                    throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString());
                } 
            }
 
            if (!(start.TextContainer is FixedTextContainer || 
                start.TextContainer is DocumentSequenceTextContainer))
                throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString()); 

            return textSegments;
        }
 
        /// 
        /// Checks if the selection object satisfies the requirements 
        /// for this processor 
        /// 
        /// selection 
        /// ITextRange interface, implemented by the object
        private TextAnchor CheckAnchor(object selection)
        {
            if (selection == null) 
                throw new ArgumentNullException("selection");
 
            TextAnchor anchor = selection as TextAnchor; 

            if (anchor == null || !(anchor.Start.TextContainer is FixedTextContainer || 
                    anchor.Start.TextContainer is DocumentSequenceTextContainer))
            {
                throw new ArgumentException(SR.Get(SRID.WrongSelectionType), "selection: type=" + selection.GetType().ToString());
            } 

            return anchor; 
        } 

        ///  
        ///     Extracts the values of attributes from a locator part.
        /// 
        /// the locator part to extract values from
        /// number of segment value to retrieve 
        /// the start point value based on StartXAttribute and StartYAttribute values
        /// the end point value based on EndXAttribyte and EndYattribute values 
        private void GetLocatorPartSegmentValues(ContentLocatorPart locatorPart, int segmentNumber, out Point start, out Point end) 
        {
            if (locatorPart == null) 
                throw new ArgumentNullException("locatorPart");

            if (FixedTextElementName != locatorPart.PartType)
                throw new ArgumentException(SR.Get(SRID.IncorrectLocatorPartType, locatorPart.PartType.Namespace + ":" + locatorPart.PartType.Name), "locatorPart"); 

            string segmentValue = locatorPart.NameValuePairs[TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)]; 
            if (segmentValue == null) 
                throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
 
            string[] values = segmentValue.Split(TextSelectionProcessor.Separator);
            if (values.Length != 4)
                throw new ArgumentException(SR.Get(SRID.InvalidLocatorPart, TextSelectionProcessor.SegmentAttribute + segmentNumber.ToString(NumberFormatInfo.InvariantInfo)));
            start = GetPoint(values[0], values[1]); 
            end = GetPoint(values[2], values[3]);
        } 
 
        /// 
        /// Calculate Point out of string X and Y values 
        /// 
        /// x string value
        /// y string value
        ///  
        private Point GetPoint(string xstr, string ystr)
        { 
            Point point; 
            if (xstr != null && !String.IsNullOrEmpty(xstr.Trim()) && ystr != null && !String.IsNullOrEmpty(ystr.Trim()))
            { 
                double x = Double.Parse(xstr, NumberFormatInfo.InvariantInfo);
                double y = Double.Parse(ystr, NumberFormatInfo.InvariantInfo);
                point = new Point(x, y);
            } 
            else
            { 
                point = new Point(double.NaN, double.NaN); 
            }
 
            return point;

        }
 
        /// 
        /// Gets the first visible TP on a DocumentPage 
        ///  
        /// document page
        /// The first visible TP or null if no visible TP on this page 
        private static ITextPointer FindStartVisibleTextPointer(DocumentPage documentPage)
        {
            ITextPointer start, end;
            if (!GetTextViewRange(documentPage, out start, out end)) 
                return null;
 
            if (!start.IsAtInsertionPosition && !start.MoveToNextInsertionPosition(LogicalDirection.Forward)) 
            {
                //there is no insertion point in this direction 
                return null;
            }

            //check if it is outside of the page 
            if (start.CompareTo(end) > 0)
                return null; 
 
            return start;
 
        }

        /// 
        /// Gets the last visible TP on a DocumentPage 
        /// 
        /// document page 
        /// The last visible TP or null if no visible TP on the page 
        private static ITextPointer FindEndVisibleTextPointer(DocumentPage documentPage)
        { 
            ITextPointer start, end;
            if (!GetTextViewRange(documentPage, out start, out end))
                return null;
 
            if (!end.IsAtInsertionPosition && !end.MoveToNextInsertionPosition(LogicalDirection.Backward))
            { 
                //there is no insertion point in this direction 
                return null;
            } 
            //check if it is outside of the page
            if (start.CompareTo(end) > 0)
                return null;
 
            return end;
 
        } 

        ///  
        /// Gets first and last TP on a documentPage.
        /// 
        /// the document page
        /// start TP 
        /// end TP
        /// true if there aretext segments on this page, otherwise false 
        private static bool GetTextViewRange(DocumentPage documentPage, out ITextPointer start, out ITextPointer end) 
        {
            ITextView textView; 
            start = end = null;

            // Missing pages can't produce TextPointers
            Invariant.Assert(documentPage != DocumentPage.Missing); 

            textView = ((IServiceProvider)documentPage).GetService(typeof(ITextView)) as ITextView; 
            Invariant.Assert(textView != null, "DocumentPage didn't provide a TextView."); 

            //check if there is any content 
            if ((textView.TextSegments == null) || (textView.TextSegments.Count == 0))
                return false;

            start = textView.TextSegments[0].Start.CreatePointer(LogicalDirection.Forward); 
            end = textView.TextSegments[textView.TextSegments.Count - 1].End.CreatePointer(LogicalDirection.Backward);
            Debug.Assert((start != null) && (end != null), "null start/end TextPointer on a non empty page"); 
            return true; 
        }
 
        #endregion Private Methods

        //-----------------------------------------------------
        // 
        //  Private Fields
        // 
        //------------------------------------------------------ 

        #region Private Fields 

        // Name of locator part element
        private static readonly XmlQualifiedName FixedTextElementName = new XmlQualifiedName("FixedTextRange", AnnotationXmlConstants.Namespaces.BaseSchemaNamespace);
 
        // ContentLocatorPart types understood by this processor
        private static readonly XmlQualifiedName[] LocatorPartTypeNames = 
                new XmlQualifiedName[] 
                {
                    FixedTextElementName 
                };

        #endregion Private Fields
 
        #region Internal Classes
        ///  
        /// Returned by GetSelectedNodes - one object per page spanned by the selection 
        /// 
        internal sealed class FixedPageProxy : DependencyObject 
        {
            public FixedPageProxy(DependencyObject parent, int page)
            {
                SetValue(PathNode.HiddenParentProperty, parent); 
                _page = page;
            } 
 
            public int Page
            { 
                get
                {
                    return _page;
                } 
            }
 
            public IList Segments 
            {
                get 
                {
                    return _segments;
                }
            } 

 
            int _page; 
            IList _segments = new List(1);
        } 

        /// 
        /// PointSegment represents a segment in fixed content with start and end points.
        ///  
        internal sealed class PointSegment
        { 
            ///  
            /// Creates a PointSegment with the given points.
            ///  
            internal PointSegment(Point start, Point end)
            {
                _start = start;
                _end = end; 
            }
 
            ///  
            /// The start point of the segment
            ///  
            public Point Start
            {
                get
                { 
                    return _start;
                } 
            } 

            ///  
            /// The end point of the segment
            /// 
            public Point End
            { 
                get
                { 
                    return _end; 
                }
            } 

            /// 
            /// Used to represent a non-existent point - for instance for a segment
            /// which spans an entire page there is no starting point. 
            /// 
            public static readonly Point NotAPoint = new Point(double.NaN, double.NaN); 
 
            private Point _start;
            private Point _end; 
        }

        #endregion Internal Classes
 
    }
 
} 

// 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