documentsequencetextcontainer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / documentsequencetextcontainer.cs / 1305600 / documentsequencetextcontainer.cs

                            //---------------------------------------------------------------------------- 
// 
//      Copyright (C) 2004 by Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
//      DocumentSequenceTextContainer is a TextContainer that aggregates 
//      0 or more TextContainer and expose them as single TextContainer. 
//
// History: 
//      07/14/2004 - Zhenbin Xu (ZhenbinX) - Created.
//
//---------------------------------------------------------------------------
 
#pragma warning disable 1634, 1691 // To enable presharp warning disables (#pragma suppress) below.
 
namespace System.Windows.Documents 
{
    using MS.Internal; 
    using MS.Internal.Documents;
    using System;
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Collections.Generic;
    using System.Collections.ObjectModel; 
    using System.ComponentModel; 
    using System.Diagnostics;
    using System.Windows;                // DependencyID etc. 
    using System.Text;
    using System.Windows.Threading;              // Dispatcher

    //===================================================================== 
    /// 
    /// DocumentSequenceTextContainer is a TextContainer that aggregates 
    /// 0 or more TextContainer and expose them as single TextContainer. 
    /// 
    internal sealed class DocumentSequenceTextContainer : ITextContainer 
    {
        //-------------------------------------------------------------------
        //
        // Connstructors 
        //
        //---------------------------------------------------------------------- 
 
        #region Constructors
 
        internal DocumentSequenceTextContainer(DependencyObject parent)
        {
            Debug.Assert(parent != null);
            Debug.Assert(parent is FixedDocumentSequence); 

            _parent = (FixedDocumentSequence)parent; 
            _Initialize(); 
        }
 
        #endregion Constructors

        //-------------------------------------------------------------------
        // 
        // Public Methods
        // 
        //---------------------------------------------------------------------- 
        #region Public Methods
 
        /// 
        /// 
        void ITextContainer.BeginChange()
        { 
            _changeBlockLevel++;
 
            // We'll raise the Changing event when/if we get an actual 
            // change added, inside AddChangeSegment.
        } 

        /// 
        /// 
        ///  
        void ITextContainer.BeginChangeNoUndo()
        { 
            // We don't support undo, so follow the BeginChange codepath. 
            ((ITextContainer)this).BeginChange();
        } 

        void ITextContainer.EndChange()
        {
            ((ITextContainer)this).EndChange(false /* skipEvents */); 
        }
 
        ///  
        /// 
        void ITextContainer.EndChange(bool skipEvents) 
        {
            TextContainerChangedEventArgs changes;

            Invariant.Assert(_changeBlockLevel > 0, "Unmatched EndChange call!"); 

            _changeBlockLevel--; 
 
            if (_changeBlockLevel == 0 &&
                _changes != null) 
            {
                changes = _changes;
                _changes = null;
 
                if (this.Changed != null && !skipEvents)
                { 
                    Changed(this, changes); 
                }
            } 
        }

        // Allocate a new ITextPointer at the specified offset.
        // Equivalent to this.Start.CreatePointer(offset), but does not 
        // necessarily allocate this.Start.
        ITextPointer ITextContainer.CreatePointerAtOffset(int offset, LogicalDirection direction) 
        { 
            return ((ITextContainer)this).Start.CreatePointer(offset, direction);
        } 

        // Not Implemented.
        ITextPointer ITextContainer.CreatePointerAtCharOffset(int charOffset, LogicalDirection direction)
        { 
            throw new NotImplementedException();
        } 
 
        ITextPointer ITextContainer.CreateDynamicTextPointer(StaticTextPointer position, LogicalDirection direction)
        { 
            return ((ITextPointer)position.Handle0).CreatePointer(direction);
        }

        StaticTextPointer ITextContainer.CreateStaticPointerAtOffset(int offset) 
        {
            return new StaticTextPointer(this, ((ITextContainer)this).CreatePointerAtOffset(offset, LogicalDirection.Forward)); 
        } 

        TextPointerContext ITextContainer.GetPointerContext(StaticTextPointer pointer, LogicalDirection direction) 
        {
            return ((ITextPointer)pointer.Handle0).GetPointerContext(direction);
        }
 
        int ITextContainer.GetOffsetToPosition(StaticTextPointer position1, StaticTextPointer position2)
        { 
            return ((ITextPointer)position1.Handle0).GetOffsetToPosition((ITextPointer)position2.Handle0); 
        }
 
        int ITextContainer.GetTextInRun(StaticTextPointer position, LogicalDirection direction, char[] textBuffer, int startIndex, int count)
        {
            return ((ITextPointer)position.Handle0).GetTextInRun(direction, textBuffer, startIndex, count);
        } 

        object ITextContainer.GetAdjacentElement(StaticTextPointer position, LogicalDirection direction) 
        { 
            return ((ITextPointer)position.Handle0).GetAdjacentElement(direction);
        } 

        DependencyObject ITextContainer.GetParent(StaticTextPointer position)
        {
            return null; 
        }
 
        StaticTextPointer ITextContainer.CreatePointer(StaticTextPointer position, int offset) 
        {
            return new StaticTextPointer(this, ((ITextPointer)position.Handle0).CreatePointer(offset)); 
        }

        StaticTextPointer ITextContainer.GetNextContextPosition(StaticTextPointer position, LogicalDirection direction)
        { 
            return new StaticTextPointer(this, ((ITextPointer)position.Handle0).GetNextContextPosition(direction));
        } 
 
        int ITextContainer.CompareTo(StaticTextPointer position1, StaticTextPointer position2)
        { 
            return ((ITextPointer)position1.Handle0).CompareTo((ITextPointer)position2.Handle0);
        }

        int ITextContainer.CompareTo(StaticTextPointer position1, ITextPointer position2) 
        {
            return ((ITextPointer)position1.Handle0).CompareTo(position2); 
        } 

        object ITextContainer.GetValue(StaticTextPointer position, DependencyProperty formattingProperty) 
        {
            return ((ITextPointer)position.Handle0).GetValue(formattingProperty);
        }
 
#if DEBUG
        ///  
        /// Debug only ToString override. 
        /// 
        public override string ToString() 
        {
            int blocks = 0;
            ChildDocumentBlock cdb = this._doclistHead;
            while (cdb != null) 
            {
                blocks++; 
                cdb = cdb.NextBlock; 
            }
            return  "DSTC Id=" + DebugId + " Blocks= " + blocks; 
        }
#endif // DEBUG
        #endregion Public Methods
 
        //--------------------------------------------------------------------
        // 
        // Public Properties 
        //
        //--------------------------------------------------------------------- 

        #region Public Properties

        ///  
        /// Specifies whether or not the content of this TextContainer may be
        /// modified. 
        ///  
        /// 
        /// True if content may be modified, false otherwise. 
        /// 
        /// 
        /// This TextContainer implementation always returns true.
        ///  
        bool ITextContainer.IsReadOnly
        { 
            get 
            {
                return true; 
            }
        }

        ///  
        /// 
        ///  
        ITextPointer ITextContainer.Start 
        {
            get 
            {
                Debug.Assert(_start != null);
                return _start;
            } 
        }
 
 
        /// 
        ///  
        /// 
        ITextPointer ITextContainer.End
        {
            get 
            {
                Debug.Assert(_end != null); 
                return _end; 
            }
        } 

        /// 
        /// Autoincremented counter of content changes in this TextContainer
        ///  
        uint ITextContainer.Generation
        { 
            get 
            {
                // For read-only content, return some constant value. 
                return 0;
            }
        }
 
        /// 
        /// Collection of highlights applied to TextContainer content. 
        ///  
        Highlights ITextContainer.Highlights
        { 
            get
            {
                return this.Highlights;
            } 
        }
 
        ///  
        /// 
        ///  
        DependencyObject ITextContainer.Parent
        {
            get { return _parent; }
        } 

        // TextEditor owns setting and clearing this property inside its 
        // ctor/OnDetach methods. 
        ITextSelection ITextContainer.TextSelection
        { 
            get { return this.TextSelection; }
            set { _textSelection = value; }
        }
 
        // Optional undo manager, always null for this ITextContainer.
        UndoManager ITextContainer.UndoManager { get { return null; } } 
 
        // 
        /// Collection of highlights applied to TextContainer content.
        ///  
        internal Highlights Highlights
        { 
            get 
            {
                if (_highlights == null) 
                {
                    _highlights = new DocumentSequenceHighlights(this);
                }
 
                return _highlights;
            } 
        } 

        // TextSelection associated with this container. 
        internal ITextSelection TextSelection
        {
            get
            { 
                return _textSelection;
            } 
        } 

 
#if DEBUG
        internal uint DebugId
        {
            get 
            {
                return _debugId; 
            } 
        }
#endif 
        #endregion Internal Properties

        //-------------------------------------------------------------------
        // 
        // Private Methods
        // 
        //---------------------------------------------------------------------- 

        #region Private Methods 

        //-------------------------------------------------------------------
        // Initilization
        //--------------------------------------------------------------------- 
        private void _Initialize()
        { 
            Debug.Assert(_parent != null); 

            // Create Start Block/Container/Position 
            _doclistHead = new ChildDocumentBlock(this, new NullTextContainer());

            // Create End Block/Container/Position
            _doclistTail = new ChildDocumentBlock(this, new NullTextContainer()); 

            // Link Start and End container together 
            _doclistHead.InsertNextBlock(_doclistTail); 

            // Now initialize the child doc block list 
            ChildDocumentBlock currentBlock = _doclistHead;
            foreach (DocumentReference docRef in _parent.References)
            {
                currentBlock.InsertNextBlock(new ChildDocumentBlock(this, docRef)); 
                currentBlock = currentBlock.NextBlock;
            } 
 
            //if we have at least one document, start and end pointers should be set to valid child blocks not to the placeholders
            if (_parent.References.Count != 0) 
            {
                _start = new DocumentSequenceTextPointer(_doclistHead.NextBlock,  _doclistHead.NextBlock.ChildContainer.Start);
                _end = new DocumentSequenceTextPointer(_doclistTail.PreviousBlock, _doclistTail.PreviousBlock.ChildContainer.End);
            } 
            else
            { 
                _start = new DocumentSequenceTextPointer(_doclistHead,  _doclistHead.ChildContainer.Start); 
                _end = new DocumentSequenceTextPointer(_doclistTail, _doclistTail.ChildContainer.End);
            } 

            // Listen to collection changes
            _parent.References.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnContentChanged);
 
            // Listen to Highlights changes so that it can notify sub-TextContainer
            this.Highlights.Changed += new HighlightChangedEventHandler(_OnHighlightChanged); 
        } 

        private void AddChange(ITextPointer startPosition, int symbolCount, PrecursorTextChangeType precursorTextChange) 
        {
            Invariant.Assert(!_isReadOnly, "Illegal to modify DocumentSequenceTextContainer inside Change event scope!");

            ITextContainer textContainer = (ITextContainer)this; 
            textContainer.BeginChange();
            try 
            { 
                // Contact any listeners.
                if (this.Changing != null) 
                {
                    Changing(this, EventArgs.Empty);
                }
 
                // Fire the ChangingEvent now if we haven't already.
                if (_changes == null) 
                { 
                    _changes = new TextContainerChangedEventArgs();
                } 

                _changes.AddChange(precursorTextChange, DocumentSequenceTextPointer.GetOffsetToPosition(_start, startPosition), symbolCount, false /* collectTextChanges */);

                if (this.Change != null) 
                {
                    Invariant.Assert(precursorTextChange == PrecursorTextChangeType.ContentAdded || precursorTextChange == PrecursorTextChangeType.ContentRemoved); 
                    TextChangeType textChange = (precursorTextChange == PrecursorTextChangeType.ContentAdded) ? 
                        TextChangeType.ContentAdded : TextChangeType.ContentRemoved;
 
                    _isReadOnly = true;
                    try
                    {
                        // Pass in a -1 for charCount parameter.  DocumentSequenceTextContainer 
                        // doesn't support this feature because it is only consumed by IMEs
                        // which never run on read-only documents. 
                        Change(this, new TextContainerChangeEventArgs(startPosition, symbolCount, -1, textChange)); 
                    }
                    finally 
                    {
                        _isReadOnly = false;
                    }
                } 
            }
            finally 
            { 
                textContainer.EndChange();
            } 
        }

        //-------------------------------------------------------------------
        // ContentChange 
        //----------------------------------------------------------------------
        private void _OnContentChanged(object sender, NotifyCollectionChangedEventArgs args) 
        { 
#if DEBUG
            this._generation++; 
#endif

            if (args.Action == NotifyCollectionChangedAction.Add)
            { 
                if (args.NewItems.Count != 1)
                { 
                    throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); 
                }
                else 
                {
                        object item = args.NewItems[0];
                        int startingIndex = args.NewStartingIndex;
 
                        if (startingIndex != _parent.References.Count - 1)
                        { 
                            throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); 
                        }
 
                        ChildDocumentBlock newBlock = new ChildDocumentBlock(this, (DocumentReference)item);

                        ChildDocumentBlock insertAfter = _doclistTail.PreviousBlock;
                        insertAfter.InsertNextBlock(newBlock); 
                        DocumentSequenceTextPointer changeStart =
                            new DocumentSequenceTextPointer(insertAfter, insertAfter.End); 
 
                        //Update end pointer
                        _end = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.End); 

                        if (newBlock.NextBlock == _doclistTail && newBlock.PreviousBlock == _doclistHead)
                        {
                            //Update start pointer for the first block 
                            _start = new DocumentSequenceTextPointer(newBlock,  newBlock.ChildContainer.Start);
 
                        } 

 
                        // Record Change Notifications
                        ITextContainer container = newBlock.ChildContainer;
                        int symbolCount = 1; // takes too long to calculate for large documents, and no one will use this info
 
                        // this does not affect state, only fires event handlers
                        AddChange(changeStart, symbolCount, PrecursorTextChangeType.ContentAdded); 
                  } 
            }
            else 
            {
                throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action));
            }
        } 

        //------------------------------------------------------------------- 
        // Highlight compositing 
        //----------------------------------------------------------------------
 
        private void _OnHighlightChanged(object sender, HighlightChangedEventArgs args)
        {
            Debug.Assert(sender != null);
            Debug.Assert(args != null); 
            Debug.Assert(args.Ranges != null);
#if DEBUG 
            { 
                Highlights highlights = this.Highlights;
                StaticTextPointer highlightTransitionPosition; 
                StaticTextPointer highlightRangeStart;
                object selected;

                DocumentsTrace.FixedDocumentSequence.Highlights.Trace("===BeginNewHighlightRange==="); 
                highlightTransitionPosition = ((ITextContainer)this).CreateStaticPointerAtOffset(0);
                while (true) 
                { 
                    // Move to the next highlight start.
                    if (!highlights.IsContentHighlighted(highlightTransitionPosition, LogicalDirection.Forward)) 
                    {
                        highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward);

                        // No more highlights? 
                        if (highlightTransitionPosition.IsNull)
                            break; 
                    } 

                    // highlightTransitionPosition is at the start of a new highlight run. 
                    selected = highlights.GetHighlightValue(highlightTransitionPosition, LogicalDirection.Forward, typeof(TextSelection));

                    // Save the start position and find the end.
                    highlightRangeStart = highlightTransitionPosition; 
                    highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward);
                    Invariant.Assert(!highlightTransitionPosition.IsNull, "Highlight start not followed by highlight end!"); 
 
                    // Store the highlight.
                    if (selected != DependencyProperty.UnsetValue) 
                    {
                        DocumentsTrace.FixedDocumentSequence.Highlights.Trace(string.Format("HightlightRange {0}-{1}", highlightRangeStart.ToString(), highlightTransitionPosition.ToString()));
                        if (highlightRangeStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
                        { 
                            DocumentsTrace.FixedDocumentSequence.Highlights.Trace("");
                        } 
                        else 
                        {
                            char[] sb = new char[256]; 
                            TextPointerBase.GetTextWithLimit(highlightRangeStart.CreateDynamicTextPointer(LogicalDirection.Forward), LogicalDirection.Forward, sb, 0, 256, highlightTransitionPosition.CreateDynamicTextPointer(LogicalDirection.Forward));
                            DocumentsTrace.FixedDocumentSequence.TextOM.Trace(string.Format("HightlightContent [{0}]", new String(sb)));
                        }
                    } 
                }
                DocumentsTrace.FixedDocumentSequence.TextOM.Trace("===EndNewHighlightRange==="); 
            } 
#endif
            Debug.Assert(args.Ranges.Count > 0 && ((TextSegment)args.Ranges[0]).Start.CompareTo(((TextSegment)args.Ranges[0]).End) < 0); 


            // For each change range we received, we need to figure out
            // affected child TextContainer, and notify it with appropriate 
            // ranges that are in the child's address space.
            // 
            // We only fire one highlight change notification for any child 
            // TextContainer even if there is multiple change ranges fall
            // into the same child TextContainer. 
            //
            // We scan the ranges and the child TextContainer in the same loop,
            // moving forward two scanning pointers and at boundary of each
            // TextContainer, we fire a change notification. 
            //
            int idxScan = 0; 
            DocumentSequenceTextPointer  tsScan  = null; 
            ChildDocumentBlock cdbScan =  null;
            List  rangeArray = new List(4); 
            while (idxScan < args.Ranges.Count)
            {
                TextSegment ts = (TextSegment)args.Ranges[idxScan];
                DocumentSequenceTextPointer tsEnd   = (DocumentSequenceTextPointer)ts.End; 
                ITextPointer tpChildStart, tpChildEnd;
                ChildDocumentBlock lastBlock; 
 
                // If tsScan == null, we were done with previous range,
                // so we are going to set tsScan to begining of this range. 
                // Otherwise the previous range was split so we will simply
                // start from what was left over from previous loop.
                if (tsScan == null)
                { 
                    tsScan = (DocumentSequenceTextPointer)ts.Start;
                } 
                lastBlock = cdbScan; 
                cdbScan = tsScan.ChildBlock;
 
                if (lastBlock != null && cdbScan != lastBlock && !(lastBlock.ChildContainer is NullTextContainer) && rangeArray.Count != 0)
                {
                    // This range is in a different block, so take care of old blocks first
                    lastBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray)); 
                    rangeArray.Clear();
                } 
 
                tpChildStart = tsScan.ChildPointer;
 
                if (tsEnd.ChildBlock != cdbScan)
                {
                    // If this range crosses blocks, we are done with current block
                    tpChildEnd = tsScan.ChildPointer.TextContainer.End; 
                    if (tpChildStart.CompareTo(tpChildEnd) != 0)
                    { 
                        rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); 
                    }
                    // Notify child container 
                    if (!(cdbScan.ChildContainer is NullTextContainer) && rangeArray.Count != 0)
                    {
                        cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray));
                    } 

                    // Move on to next block; 
                    cdbScan = cdbScan.NextBlock; 
                    tsScan  = new DocumentSequenceTextPointer(cdbScan, cdbScan.ChildContainer.Start);
                    rangeArray.Clear(); 
                }
                else
                {
                    // Otherwise we need to go on to see if there is more ranges 
                    // fall withing the same block. Simply add this change range
                    tpChildEnd = tsEnd.ChildPointer; 
                    if (tpChildStart.CompareTo(tpChildEnd) != 0) 
                    {
                        rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); 
                    }

                    // Move on to next range
                    idxScan++; 
                    tsScan = null;
                } 
            } 

            // Fire change notification for the last child block. 
            if (rangeArray.Count > 0 && (!(cdbScan == null || cdbScan.ChildContainer is NullTextContainer)))
            {
                cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray));
            } 
        }
        #endregion Private Methods 
 
        //--------------------------------------------------------------------
        // 
        // Private Fields
        //
        //---------------------------------------------------------------------
 
        #region Private Fields
        private readonly FixedDocumentSequence   _parent;    // The content aggregator, supplied in ctor 
        private DocumentSequenceTextPointer _start;    // Start of the aggregated TextContainer 
        private DocumentSequenceTextPointer _end;      // End of the aggregated TextContainer
        private ChildDocumentBlock  _doclistHead;       // Head of the doubly linked list of child TextContainer entries 
        private ChildDocumentBlock  _doclistTail;       // Tail of the doubly linked list of child TextContainer entries
        private ITextSelection _textSelection;

        // Collection of highlights applied to TextContainer content. 
        private Highlights _highlights;
 
        // BeginChange ref count.  When non-zero, we are inside a change block. 
        private int _changeBlockLevel;
 
        // Array of pending changes in the current change block.
        // Null outside of a change block.
        private TextContainerChangedEventArgs _changes;
 
        // TextView associated with this TextContainer.
        private ITextView _textview; 
 
        // Set true during Change event callback.
        // When true, modifying the TextContainer is disallowed. 
        private bool _isReadOnly;

#if DEBUG
        // The container generation, increamented whenever container content changes. 
        private uint  _generation;
        private uint _debugId = (DocumentSequenceTextContainer._debugIdCounter++); 
        private static uint _debugIdCounter = 0; 
#endif
        #endregion Private Fields 


        //--------------------------------------------------------------------
        // 
        // Private Classes
        // 
        //--------------------------------------------------------------------- 

        #region Private Classes 

        /// 
        /// DocumentSequence specific Highlights subclass accepts text pointers from
        /// either the DocSequence or any of its child FixedDocuments.  This allows 
        /// the highlights to be set on the DocSequence but the FixedDocuments continue
        /// to handle the rendering (FixedDocs look for their parent's Highlights if 
        /// the parent is a DocSequence). 
        /// 
        private sealed class DocumentSequenceHighlights : Highlights 
        {
            internal DocumentSequenceHighlights(DocumentSequenceTextContainer textContainer)
                : base(textContainer)
            { 
            }
 
            ///  
            /// Returns the value of a property stored on scoping highlight, if any.
            ///  
            /// 
            /// Position to query.
            /// 
            ///  
            /// Direction of content to query.
            ///  
            ///  
            /// Type of the matching highlight layer owner.
            ///  
            /// 
            /// The highlight value if set on any scoping highlight.  If no property
            /// value is set, returns DependencyProperty.UnsetValue.
            ///  
            internal override object GetHighlightValue(StaticTextPointer textPosition, LogicalDirection direction, Type highlightLayerOwnerType)
            { 
                StaticTextPointer parentPosition; 

                if (EnsureParentPosition(textPosition, direction, out parentPosition)) 
                {
                    return base.GetHighlightValue(parentPosition, direction, highlightLayerOwnerType);
                }
 
                return DependencyProperty.UnsetValue;
            } 
 
            /// 
            /// Returns true iff the indicated content has scoping highlights. 
            /// 
            /// 
            /// Position to query.
            ///  
            /// 
            /// Direction of content to query. 
            ///  
            internal override bool IsContentHighlighted(StaticTextPointer textPosition, LogicalDirection direction)
            { 
                StaticTextPointer parentPosition;

                if (EnsureParentPosition(textPosition, direction, out parentPosition))
                { 
                    return base.IsContentHighlighted(parentPosition, direction);
                } 
 
                return false;
            } 

            /// 
            /// Returns the position of the next highlight start or end in an
            /// indicated direction, or null if there is no such position. 
            /// 
            ///  
            /// Position to query. 
            /// 
            ///  
            /// Direction of content to query.
            /// 
            internal override StaticTextPointer GetNextHighlightChangePosition(StaticTextPointer textPosition, LogicalDirection direction)
            { 
                StaticTextPointer parentPosition;
                StaticTextPointer returnPointer = StaticTextPointer.Null; 
 
                if (EnsureParentPosition(textPosition, direction, out parentPosition))
                { 
                    returnPointer = base.GetNextHighlightChangePosition(parentPosition, direction);

                    // If we were passed a child position, we need to convert the result back to a child position
                    if (textPosition.TextContainer.Highlights != this) 
                    {
                        returnPointer = GetStaticPositionInChildContainer(returnPointer, direction, textPosition); 
                    } 
                }
 
                return returnPointer;
            }

            ///  
            /// Returns the closest neighboring TextPointer in an indicated
            /// direction where a property value calculated from an embedded 
            /// object, scoping text element, or scoping highlight could 
            /// change.
            ///  
            /// 
            /// Position to query.
            /// 
            ///  
            /// Direction of content to query.
            ///  
            ///  
            /// If the following symbol is TextPointerContext.EmbeddedElement,
            /// TextPointerContext.ElementBegin, or TextPointerContext.ElementEnd, returns 
            /// a TextPointer exactly one symbol distant.
            ///
            /// If the following symbol is TextPointerContext.Text, the distance
            /// of the returned TextPointer is the minimum of the value returned 
            /// by textPosition.GetTextLength and the distance to any highlight
            /// start or end edge. 
            /// 
            /// If the following symbol is TextPointerContext.None, returns null.
            ///  
            internal override StaticTextPointer GetNextPropertyChangePosition(StaticTextPointer textPosition, LogicalDirection direction)
            {
                StaticTextPointer parentPosition;
                StaticTextPointer returnPointer = StaticTextPointer.Null; 

                if (EnsureParentPosition(textPosition, direction, out parentPosition)) 
                { 
                    returnPointer = base.GetNextPropertyChangePosition(parentPosition, direction);
 
                    // If we were passed a child position, we need to convert the result back to a child position
                    if (textPosition.TextContainer.Highlights != this)
                    {
                        returnPointer = GetStaticPositionInChildContainer(returnPointer, direction, textPosition); 
                    }
                } 
 
                return returnPointer;
            } 

            /// 
            /// Sets parentPosition to be a valid TextPointer in the parent document.  This could either
            /// be the textPosition passed in (if its already on the parent document) or a conversion 
            /// of the textPosition passed in.
            ///  
            /// whether or not parentPosition is valid and should be used 
            private bool EnsureParentPosition(StaticTextPointer textPosition, LogicalDirection direction, out StaticTextPointer parentPosition)
            { 
                // Simple case - textPosition is already in the parent TextContainer
                parentPosition = textPosition;

                // If textPosition is on a child TextContainer, we convert it 
                if (textPosition.TextContainer.Highlights != this)
                { 
                    // This case can't be converted so return false, out parameter should not be used 
                    if (textPosition.GetPointerContext(direction) == TextPointerContext.None)
                        return false; 

                    // Turn the textPosition (which should be in the scope of a FixedDocument)
                    // into a position in the scope of the DocumentSequence.
                    ITextPointer dynamicTextPointer = textPosition.CreateDynamicTextPointer(LogicalDirection.Forward); 
                    ITextPointer parentTextPointer = ((DocumentSequenceTextContainer)this.TextContainer).MapChildPositionToParent(dynamicTextPointer);
                    Debug.Assert(parentTextPointer != null); 
                    parentPosition = parentTextPointer.CreateStaticPointer(); 
                }
 
                // Returning true - either we started with a parent position or we converted to one
                return true;
            }
 
            /// 
            /// Conversion from a StaticTextPointer on a DocumentSequence into a StaticTextPointer 
            /// on a specified FixedDocument.  If the conversion results in a pointer on a different 
            /// FixedDocument then we return one end of the FixedDocument (based on direction).
            ///  
            /// position in a DocumentSequence to convert
            /// direction of the desired conversion
            /// original pointer from FixedDocument
            private StaticTextPointer GetStaticPositionInChildContainer(StaticTextPointer textPosition, LogicalDirection direction, StaticTextPointer originalPosition) 
            {
                StaticTextPointer parentTextPointer = StaticTextPointer.Null; 
 
                if (!textPosition.IsNull)
                { 
                    DocumentSequenceTextPointer parentChangePosition = textPosition.CreateDynamicTextPointer(LogicalDirection.Forward) as DocumentSequenceTextPointer;
                    Debug.Assert(parentChangePosition != null);

                    // If the DocSequence position translates into a position in a different FixedDocument than 
                    // the original request, we return an end of the original FixedDocument (which end depends on direction)
                    ITextPointer childTp = parentChangePosition.ChildPointer; 
                    if (childTp.TextContainer != originalPosition.TextContainer) 
                    {
                        // If the position we started searching from is highlighted, cut the highlight 
                        // at the end of the text container.  Otherwise return null (the highlight must
                        // start in the next document).
                        if (IsContentHighlighted(originalPosition, direction))
                        { 
                            childTp = direction == LogicalDirection.Forward ?
                                                   originalPosition.TextContainer.End 
                                                 : originalPosition.TextContainer.Start; 
                            parentTextPointer = childTp.CreateStaticPointer();
                        } 
                        else
                        {
                            parentTextPointer = StaticTextPointer.Null;
                        } 
                    }
                    else 
                    { 
                        parentTextPointer = childTp.CreateStaticPointer();
                    } 
                }

                return parentTextPointer;
            } 
        }
 
        #endregion Private Classes 

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
// 
//      Copyright (C) 2004 by Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
//      DocumentSequenceTextContainer is a TextContainer that aggregates 
//      0 or more TextContainer and expose them as single TextContainer. 
//
// History: 
//      07/14/2004 - Zhenbin Xu (ZhenbinX) - Created.
//
//---------------------------------------------------------------------------
 
#pragma warning disable 1634, 1691 // To enable presharp warning disables (#pragma suppress) below.
 
namespace System.Windows.Documents 
{
    using MS.Internal; 
    using MS.Internal.Documents;
    using System;
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Collections.Generic;
    using System.Collections.ObjectModel; 
    using System.ComponentModel; 
    using System.Diagnostics;
    using System.Windows;                // DependencyID etc. 
    using System.Text;
    using System.Windows.Threading;              // Dispatcher

    //===================================================================== 
    /// 
    /// DocumentSequenceTextContainer is a TextContainer that aggregates 
    /// 0 or more TextContainer and expose them as single TextContainer. 
    /// 
    internal sealed class DocumentSequenceTextContainer : ITextContainer 
    {
        //-------------------------------------------------------------------
        //
        // Connstructors 
        //
        //---------------------------------------------------------------------- 
 
        #region Constructors
 
        internal DocumentSequenceTextContainer(DependencyObject parent)
        {
            Debug.Assert(parent != null);
            Debug.Assert(parent is FixedDocumentSequence); 

            _parent = (FixedDocumentSequence)parent; 
            _Initialize(); 
        }
 
        #endregion Constructors

        //-------------------------------------------------------------------
        // 
        // Public Methods
        // 
        //---------------------------------------------------------------------- 
        #region Public Methods
 
        /// 
        /// 
        void ITextContainer.BeginChange()
        { 
            _changeBlockLevel++;
 
            // We'll raise the Changing event when/if we get an actual 
            // change added, inside AddChangeSegment.
        } 

        /// 
        /// 
        ///  
        void ITextContainer.BeginChangeNoUndo()
        { 
            // We don't support undo, so follow the BeginChange codepath. 
            ((ITextContainer)this).BeginChange();
        } 

        void ITextContainer.EndChange()
        {
            ((ITextContainer)this).EndChange(false /* skipEvents */); 
        }
 
        ///  
        /// 
        void ITextContainer.EndChange(bool skipEvents) 
        {
            TextContainerChangedEventArgs changes;

            Invariant.Assert(_changeBlockLevel > 0, "Unmatched EndChange call!"); 

            _changeBlockLevel--; 
 
            if (_changeBlockLevel == 0 &&
                _changes != null) 
            {
                changes = _changes;
                _changes = null;
 
                if (this.Changed != null && !skipEvents)
                { 
                    Changed(this, changes); 
                }
            } 
        }

        // Allocate a new ITextPointer at the specified offset.
        // Equivalent to this.Start.CreatePointer(offset), but does not 
        // necessarily allocate this.Start.
        ITextPointer ITextContainer.CreatePointerAtOffset(int offset, LogicalDirection direction) 
        { 
            return ((ITextContainer)this).Start.CreatePointer(offset, direction);
        } 

        // Not Implemented.
        ITextPointer ITextContainer.CreatePointerAtCharOffset(int charOffset, LogicalDirection direction)
        { 
            throw new NotImplementedException();
        } 
 
        ITextPointer ITextContainer.CreateDynamicTextPointer(StaticTextPointer position, LogicalDirection direction)
        { 
            return ((ITextPointer)position.Handle0).CreatePointer(direction);
        }

        StaticTextPointer ITextContainer.CreateStaticPointerAtOffset(int offset) 
        {
            return new StaticTextPointer(this, ((ITextContainer)this).CreatePointerAtOffset(offset, LogicalDirection.Forward)); 
        } 

        TextPointerContext ITextContainer.GetPointerContext(StaticTextPointer pointer, LogicalDirection direction) 
        {
            return ((ITextPointer)pointer.Handle0).GetPointerContext(direction);
        }
 
        int ITextContainer.GetOffsetToPosition(StaticTextPointer position1, StaticTextPointer position2)
        { 
            return ((ITextPointer)position1.Handle0).GetOffsetToPosition((ITextPointer)position2.Handle0); 
        }
 
        int ITextContainer.GetTextInRun(StaticTextPointer position, LogicalDirection direction, char[] textBuffer, int startIndex, int count)
        {
            return ((ITextPointer)position.Handle0).GetTextInRun(direction, textBuffer, startIndex, count);
        } 

        object ITextContainer.GetAdjacentElement(StaticTextPointer position, LogicalDirection direction) 
        { 
            return ((ITextPointer)position.Handle0).GetAdjacentElement(direction);
        } 

        DependencyObject ITextContainer.GetParent(StaticTextPointer position)
        {
            return null; 
        }
 
        StaticTextPointer ITextContainer.CreatePointer(StaticTextPointer position, int offset) 
        {
            return new StaticTextPointer(this, ((ITextPointer)position.Handle0).CreatePointer(offset)); 
        }

        StaticTextPointer ITextContainer.GetNextContextPosition(StaticTextPointer position, LogicalDirection direction)
        { 
            return new StaticTextPointer(this, ((ITextPointer)position.Handle0).GetNextContextPosition(direction));
        } 
 
        int ITextContainer.CompareTo(StaticTextPointer position1, StaticTextPointer position2)
        { 
            return ((ITextPointer)position1.Handle0).CompareTo((ITextPointer)position2.Handle0);
        }

        int ITextContainer.CompareTo(StaticTextPointer position1, ITextPointer position2) 
        {
            return ((ITextPointer)position1.Handle0).CompareTo(position2); 
        } 

        object ITextContainer.GetValue(StaticTextPointer position, DependencyProperty formattingProperty) 
        {
            return ((ITextPointer)position.Handle0).GetValue(formattingProperty);
        }
 
#if DEBUG
        ///  
        /// Debug only ToString override. 
        /// 
        public override string ToString() 
        {
            int blocks = 0;
            ChildDocumentBlock cdb = this._doclistHead;
            while (cdb != null) 
            {
                blocks++; 
                cdb = cdb.NextBlock; 
            }
            return  "DSTC Id=" + DebugId + " Blocks= " + blocks; 
        }
#endif // DEBUG
        #endregion Public Methods
 
        //--------------------------------------------------------------------
        // 
        // Public Properties 
        //
        //--------------------------------------------------------------------- 

        #region Public Properties

        ///  
        /// Specifies whether or not the content of this TextContainer may be
        /// modified. 
        ///  
        /// 
        /// True if content may be modified, false otherwise. 
        /// 
        /// 
        /// This TextContainer implementation always returns true.
        ///  
        bool ITextContainer.IsReadOnly
        { 
            get 
            {
                return true; 
            }
        }

        ///  
        /// 
        ///  
        ITextPointer ITextContainer.Start 
        {
            get 
            {
                Debug.Assert(_start != null);
                return _start;
            } 
        }
 
 
        /// 
        ///  
        /// 
        ITextPointer ITextContainer.End
        {
            get 
            {
                Debug.Assert(_end != null); 
                return _end; 
            }
        } 

        /// 
        /// Autoincremented counter of content changes in this TextContainer
        ///  
        uint ITextContainer.Generation
        { 
            get 
            {
                // For read-only content, return some constant value. 
                return 0;
            }
        }
 
        /// 
        /// Collection of highlights applied to TextContainer content. 
        ///  
        Highlights ITextContainer.Highlights
        { 
            get
            {
                return this.Highlights;
            } 
        }
 
        ///  
        /// 
        ///  
        DependencyObject ITextContainer.Parent
        {
            get { return _parent; }
        } 

        // TextEditor owns setting and clearing this property inside its 
        // ctor/OnDetach methods. 
        ITextSelection ITextContainer.TextSelection
        { 
            get { return this.TextSelection; }
            set { _textSelection = value; }
        }
 
        // Optional undo manager, always null for this ITextContainer.
        UndoManager ITextContainer.UndoManager { get { return null; } } 
 
        // 
        /// Collection of highlights applied to TextContainer content.
        ///  
        internal Highlights Highlights
        { 
            get 
            {
                if (_highlights == null) 
                {
                    _highlights = new DocumentSequenceHighlights(this);
                }
 
                return _highlights;
            } 
        } 

        // TextSelection associated with this container. 
        internal ITextSelection TextSelection
        {
            get
            { 
                return _textSelection;
            } 
        } 

 
#if DEBUG
        internal uint DebugId
        {
            get 
            {
                return _debugId; 
            } 
        }
#endif 
        #endregion Internal Properties

        //-------------------------------------------------------------------
        // 
        // Private Methods
        // 
        //---------------------------------------------------------------------- 

        #region Private Methods 

        //-------------------------------------------------------------------
        // Initilization
        //--------------------------------------------------------------------- 
        private void _Initialize()
        { 
            Debug.Assert(_parent != null); 

            // Create Start Block/Container/Position 
            _doclistHead = new ChildDocumentBlock(this, new NullTextContainer());

            // Create End Block/Container/Position
            _doclistTail = new ChildDocumentBlock(this, new NullTextContainer()); 

            // Link Start and End container together 
            _doclistHead.InsertNextBlock(_doclistTail); 

            // Now initialize the child doc block list 
            ChildDocumentBlock currentBlock = _doclistHead;
            foreach (DocumentReference docRef in _parent.References)
            {
                currentBlock.InsertNextBlock(new ChildDocumentBlock(this, docRef)); 
                currentBlock = currentBlock.NextBlock;
            } 
 
            //if we have at least one document, start and end pointers should be set to valid child blocks not to the placeholders
            if (_parent.References.Count != 0) 
            {
                _start = new DocumentSequenceTextPointer(_doclistHead.NextBlock,  _doclistHead.NextBlock.ChildContainer.Start);
                _end = new DocumentSequenceTextPointer(_doclistTail.PreviousBlock, _doclistTail.PreviousBlock.ChildContainer.End);
            } 
            else
            { 
                _start = new DocumentSequenceTextPointer(_doclistHead,  _doclistHead.ChildContainer.Start); 
                _end = new DocumentSequenceTextPointer(_doclistTail, _doclistTail.ChildContainer.End);
            } 

            // Listen to collection changes
            _parent.References.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnContentChanged);
 
            // Listen to Highlights changes so that it can notify sub-TextContainer
            this.Highlights.Changed += new HighlightChangedEventHandler(_OnHighlightChanged); 
        } 

        private void AddChange(ITextPointer startPosition, int symbolCount, PrecursorTextChangeType precursorTextChange) 
        {
            Invariant.Assert(!_isReadOnly, "Illegal to modify DocumentSequenceTextContainer inside Change event scope!");

            ITextContainer textContainer = (ITextContainer)this; 
            textContainer.BeginChange();
            try 
            { 
                // Contact any listeners.
                if (this.Changing != null) 
                {
                    Changing(this, EventArgs.Empty);
                }
 
                // Fire the ChangingEvent now if we haven't already.
                if (_changes == null) 
                { 
                    _changes = new TextContainerChangedEventArgs();
                } 

                _changes.AddChange(precursorTextChange, DocumentSequenceTextPointer.GetOffsetToPosition(_start, startPosition), symbolCount, false /* collectTextChanges */);

                if (this.Change != null) 
                {
                    Invariant.Assert(precursorTextChange == PrecursorTextChangeType.ContentAdded || precursorTextChange == PrecursorTextChangeType.ContentRemoved); 
                    TextChangeType textChange = (precursorTextChange == PrecursorTextChangeType.ContentAdded) ? 
                        TextChangeType.ContentAdded : TextChangeType.ContentRemoved;
 
                    _isReadOnly = true;
                    try
                    {
                        // Pass in a -1 for charCount parameter.  DocumentSequenceTextContainer 
                        // doesn't support this feature because it is only consumed by IMEs
                        // which never run on read-only documents. 
                        Change(this, new TextContainerChangeEventArgs(startPosition, symbolCount, -1, textChange)); 
                    }
                    finally 
                    {
                        _isReadOnly = false;
                    }
                } 
            }
            finally 
            { 
                textContainer.EndChange();
            } 
        }

        //-------------------------------------------------------------------
        // ContentChange 
        //----------------------------------------------------------------------
        private void _OnContentChanged(object sender, NotifyCollectionChangedEventArgs args) 
        { 
#if DEBUG
            this._generation++; 
#endif

            if (args.Action == NotifyCollectionChangedAction.Add)
            { 
                if (args.NewItems.Count != 1)
                { 
                    throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); 
                }
                else 
                {
                        object item = args.NewItems[0];
                        int startingIndex = args.NewStartingIndex;
 
                        if (startingIndex != _parent.References.Count - 1)
                        { 
                            throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); 
                        }
 
                        ChildDocumentBlock newBlock = new ChildDocumentBlock(this, (DocumentReference)item);

                        ChildDocumentBlock insertAfter = _doclistTail.PreviousBlock;
                        insertAfter.InsertNextBlock(newBlock); 
                        DocumentSequenceTextPointer changeStart =
                            new DocumentSequenceTextPointer(insertAfter, insertAfter.End); 
 
                        //Update end pointer
                        _end = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.End); 

                        if (newBlock.NextBlock == _doclistTail && newBlock.PreviousBlock == _doclistHead)
                        {
                            //Update start pointer for the first block 
                            _start = new DocumentSequenceTextPointer(newBlock,  newBlock.ChildContainer.Start);
 
                        } 

 
                        // Record Change Notifications
                        ITextContainer container = newBlock.ChildContainer;
                        int symbolCount = 1; // takes too long to calculate for large documents, and no one will use this info
 
                        // this does not affect state, only fires event handlers
                        AddChange(changeStart, symbolCount, PrecursorTextChangeType.ContentAdded); 
                  } 
            }
            else 
            {
                throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action));
            }
        } 

        //------------------------------------------------------------------- 
        // Highlight compositing 
        //----------------------------------------------------------------------
 
        private void _OnHighlightChanged(object sender, HighlightChangedEventArgs args)
        {
            Debug.Assert(sender != null);
            Debug.Assert(args != null); 
            Debug.Assert(args.Ranges != null);
#if DEBUG 
            { 
                Highlights highlights = this.Highlights;
                StaticTextPointer highlightTransitionPosition; 
                StaticTextPointer highlightRangeStart;
                object selected;

                DocumentsTrace.FixedDocumentSequence.Highlights.Trace("===BeginNewHighlightRange==="); 
                highlightTransitionPosition = ((ITextContainer)this).CreateStaticPointerAtOffset(0);
                while (true) 
                { 
                    // Move to the next highlight start.
                    if (!highlights.IsContentHighlighted(highlightTransitionPosition, LogicalDirection.Forward)) 
                    {
                        highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward);

                        // No more highlights? 
                        if (highlightTransitionPosition.IsNull)
                            break; 
                    } 

                    // highlightTransitionPosition is at the start of a new highlight run. 
                    selected = highlights.GetHighlightValue(highlightTransitionPosition, LogicalDirection.Forward, typeof(TextSelection));

                    // Save the start position and find the end.
                    highlightRangeStart = highlightTransitionPosition; 
                    highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward);
                    Invariant.Assert(!highlightTransitionPosition.IsNull, "Highlight start not followed by highlight end!"); 
 
                    // Store the highlight.
                    if (selected != DependencyProperty.UnsetValue) 
                    {
                        DocumentsTrace.FixedDocumentSequence.Highlights.Trace(string.Format("HightlightRange {0}-{1}", highlightRangeStart.ToString(), highlightTransitionPosition.ToString()));
                        if (highlightRangeStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
                        { 
                            DocumentsTrace.FixedDocumentSequence.Highlights.Trace("");
                        } 
                        else 
                        {
                            char[] sb = new char[256]; 
                            TextPointerBase.GetTextWithLimit(highlightRangeStart.CreateDynamicTextPointer(LogicalDirection.Forward), LogicalDirection.Forward, sb, 0, 256, highlightTransitionPosition.CreateDynamicTextPointer(LogicalDirection.Forward));
                            DocumentsTrace.FixedDocumentSequence.TextOM.Trace(string.Format("HightlightContent [{0}]", new String(sb)));
                        }
                    } 
                }
                DocumentsTrace.FixedDocumentSequence.TextOM.Trace("===EndNewHighlightRange==="); 
            } 
#endif
            Debug.Assert(args.Ranges.Count > 0 && ((TextSegment)args.Ranges[0]).Start.CompareTo(((TextSegment)args.Ranges[0]).End) < 0); 


            // For each change range we received, we need to figure out
            // affected child TextContainer, and notify it with appropriate 
            // ranges that are in the child's address space.
            // 
            // We only fire one highlight change notification for any child 
            // TextContainer even if there is multiple change ranges fall
            // into the same child TextContainer. 
            //
            // We scan the ranges and the child TextContainer in the same loop,
            // moving forward two scanning pointers and at boundary of each
            // TextContainer, we fire a change notification. 
            //
            int idxScan = 0; 
            DocumentSequenceTextPointer  tsScan  = null; 
            ChildDocumentBlock cdbScan =  null;
            List  rangeArray = new List(4); 
            while (idxScan < args.Ranges.Count)
            {
                TextSegment ts = (TextSegment)args.Ranges[idxScan];
                DocumentSequenceTextPointer tsEnd   = (DocumentSequenceTextPointer)ts.End; 
                ITextPointer tpChildStart, tpChildEnd;
                ChildDocumentBlock lastBlock; 
 
                // If tsScan == null, we were done with previous range,
                // so we are going to set tsScan to begining of this range. 
                // Otherwise the previous range was split so we will simply
                // start from what was left over from previous loop.
                if (tsScan == null)
                { 
                    tsScan = (DocumentSequenceTextPointer)ts.Start;
                } 
                lastBlock = cdbScan; 
                cdbScan = tsScan.ChildBlock;
 
                if (lastBlock != null && cdbScan != lastBlock && !(lastBlock.ChildContainer is NullTextContainer) && rangeArray.Count != 0)
                {
                    // This range is in a different block, so take care of old blocks first
                    lastBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray)); 
                    rangeArray.Clear();
                } 
 
                tpChildStart = tsScan.ChildPointer;
 
                if (tsEnd.ChildBlock != cdbScan)
                {
                    // If this range crosses blocks, we are done with current block
                    tpChildEnd = tsScan.ChildPointer.TextContainer.End; 
                    if (tpChildStart.CompareTo(tpChildEnd) != 0)
                    { 
                        rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); 
                    }
                    // Notify child container 
                    if (!(cdbScan.ChildContainer is NullTextContainer) && rangeArray.Count != 0)
                    {
                        cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray));
                    } 

                    // Move on to next block; 
                    cdbScan = cdbScan.NextBlock; 
                    tsScan  = new DocumentSequenceTextPointer(cdbScan, cdbScan.ChildContainer.Start);
                    rangeArray.Clear(); 
                }
                else
                {
                    // Otherwise we need to go on to see if there is more ranges 
                    // fall withing the same block. Simply add this change range
                    tpChildEnd = tsEnd.ChildPointer; 
                    if (tpChildStart.CompareTo(tpChildEnd) != 0) 
                    {
                        rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); 
                    }

                    // Move on to next range
                    idxScan++; 
                    tsScan = null;
                } 
            } 

            // Fire change notification for the last child block. 
            if (rangeArray.Count > 0 && (!(cdbScan == null || cdbScan.ChildContainer is NullTextContainer)))
            {
                cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection(rangeArray));
            } 
        }
        #endregion Private Methods 
 
        //--------------------------------------------------------------------
        // 
        // Private Fields
        //
        //---------------------------------------------------------------------
 
        #region Private Fields
        private readonly FixedDocumentSequence   _parent;    // The content aggregator, supplied in ctor 
        private DocumentSequenceTextPointer _start;    // Start of the aggregated TextContainer 
        private DocumentSequenceTextPointer _end;      // End of the aggregated TextContainer
        private ChildDocumentBlock  _doclistHead;       // Head of the doubly linked list of child TextContainer entries 
        private ChildDocumentBlock  _doclistTail;       // Tail of the doubly linked list of child TextContainer entries
        private ITextSelection _textSelection;

        // Collection of highlights applied to TextContainer content. 
        private Highlights _highlights;
 
        // BeginChange ref count.  When non-zero, we are inside a change block. 
        private int _changeBlockLevel;
 
        // Array of pending changes in the current change block.
        // Null outside of a change block.
        private TextContainerChangedEventArgs _changes;
 
        // TextView associated with this TextContainer.
        private ITextView _textview; 
 
        // Set true during Change event callback.
        // When true, modifying the TextContainer is disallowed. 
        private bool _isReadOnly;

#if DEBUG
        // The container generation, increamented whenever container content changes. 
        private uint  _generation;
        private uint _debugId = (DocumentSequenceTextContainer._debugIdCounter++); 
        private static uint _debugIdCounter = 0; 
#endif
        #endregion Private Fields 


        //--------------------------------------------------------------------
        // 
        // Private Classes
        // 
        //--------------------------------------------------------------------- 

        #region Private Classes 

        /// 
        /// DocumentSequence specific Highlights subclass accepts text pointers from
        /// either the DocSequence or any of its child FixedDocuments.  This allows 
        /// the highlights to be set on the DocSequence but the FixedDocuments continue
        /// to handle the rendering (FixedDocs look for their parent's Highlights if 
        /// the parent is a DocSequence). 
        /// 
        private sealed class DocumentSequenceHighlights : Highlights 
        {
            internal DocumentSequenceHighlights(DocumentSequenceTextContainer textContainer)
                : base(textContainer)
            { 
            }
 
            ///  
            /// Returns the value of a property stored on scoping highlight, if any.
            ///  
            /// 
            /// Position to query.
            /// 
            ///  
            /// Direction of content to query.
            ///  
            ///  
            /// Type of the matching highlight layer owner.
            ///  
            /// 
            /// The highlight value if set on any scoping highlight.  If no property
            /// value is set, returns DependencyProperty.UnsetValue.
            ///  
            internal override object GetHighlightValue(StaticTextPointer textPosition, LogicalDirection direction, Type highlightLayerOwnerType)
            { 
                StaticTextPointer parentPosition; 

                if (EnsureParentPosition(textPosition, direction, out parentPosition)) 
                {
                    return base.GetHighlightValue(parentPosition, direction, highlightLayerOwnerType);
                }
 
                return DependencyProperty.UnsetValue;
            } 
 
            /// 
            /// Returns true iff the indicated content has scoping highlights. 
            /// 
            /// 
            /// Position to query.
            ///  
            /// 
            /// Direction of content to query. 
            ///  
            internal override bool IsContentHighlighted(StaticTextPointer textPosition, LogicalDirection direction)
            { 
                StaticTextPointer parentPosition;

                if (EnsureParentPosition(textPosition, direction, out parentPosition))
                { 
                    return base.IsContentHighlighted(parentPosition, direction);
                } 
 
                return false;
            } 

            /// 
            /// Returns the position of the next highlight start or end in an
            /// indicated direction, or null if there is no such position. 
            /// 
            ///  
            /// Position to query. 
            /// 
            ///  
            /// Direction of content to query.
            /// 
            internal override StaticTextPointer GetNextHighlightChangePosition(StaticTextPointer textPosition, LogicalDirection direction)
            { 
                StaticTextPointer parentPosition;
                StaticTextPointer returnPointer = StaticTextPointer.Null; 
 
                if (EnsureParentPosition(textPosition, direction, out parentPosition))
                { 
                    returnPointer = base.GetNextHighlightChangePosition(parentPosition, direction);

                    // If we were passed a child position, we need to convert the result back to a child position
                    if (textPosition.TextContainer.Highlights != this) 
                    {
                        returnPointer = GetStaticPositionInChildContainer(returnPointer, direction, textPosition); 
                    } 
                }
 
                return returnPointer;
            }

            ///  
            /// Returns the closest neighboring TextPointer in an indicated
            /// direction where a property value calculated from an embedded 
            /// object, scoping text element, or scoping highlight could 
            /// change.
            ///  
            /// 
            /// Position to query.
            /// 
            ///  
            /// Direction of content to query.
            ///  
            ///  
            /// If the following symbol is TextPointerContext.EmbeddedElement,
            /// TextPointerContext.ElementBegin, or TextPointerContext.ElementEnd, returns 
            /// a TextPointer exactly one symbol distant.
            ///
            /// If the following symbol is TextPointerContext.Text, the distance
            /// of the returned TextPointer is the minimum of the value returned 
            /// by textPosition.GetTextLength and the distance to any highlight
            /// start or end edge. 
            /// 
            /// If the following symbol is TextPointerContext.None, returns null.
            ///  
            internal override StaticTextPointer GetNextPropertyChangePosition(StaticTextPointer textPosition, LogicalDirection direction)
            {
                StaticTextPointer parentPosition;
                StaticTextPointer returnPointer = StaticTextPointer.Null; 

                if (EnsureParentPosition(textPosition, direction, out parentPosition)) 
                { 
                    returnPointer = base.GetNextPropertyChangePosition(parentPosition, direction);
 
                    // If we were passed a child position, we need to convert the result back to a child position
                    if (textPosition.TextContainer.Highlights != this)
                    {
                        returnPointer = GetStaticPositionInChildContainer(returnPointer, direction, textPosition); 
                    }
                } 
 
                return returnPointer;
            } 

            /// 
            /// Sets parentPosition to be a valid TextPointer in the parent document.  This could either
            /// be the textPosition passed in (if its already on the parent document) or a conversion 
            /// of the textPosition passed in.
            ///  
            /// whether or not parentPosition is valid and should be used 
            private bool EnsureParentPosition(StaticTextPointer textPosition, LogicalDirection direction, out StaticTextPointer parentPosition)
            { 
                // Simple case - textPosition is already in the parent TextContainer
                parentPosition = textPosition;

                // If textPosition is on a child TextContainer, we convert it 
                if (textPosition.TextContainer.Highlights != this)
                { 
                    // This case can't be converted so return false, out parameter should not be used 
                    if (textPosition.GetPointerContext(direction) == TextPointerContext.None)
                        return false; 

                    // Turn the textPosition (which should be in the scope of a FixedDocument)
                    // into a position in the scope of the DocumentSequence.
                    ITextPointer dynamicTextPointer = textPosition.CreateDynamicTextPointer(LogicalDirection.Forward); 
                    ITextPointer parentTextPointer = ((DocumentSequenceTextContainer)this.TextContainer).MapChildPositionToParent(dynamicTextPointer);
                    Debug.Assert(parentTextPointer != null); 
                    parentPosition = parentTextPointer.CreateStaticPointer(); 
                }
 
                // Returning true - either we started with a parent position or we converted to one
                return true;
            }
 
            /// 
            /// Conversion from a StaticTextPointer on a DocumentSequence into a StaticTextPointer 
            /// on a specified FixedDocument.  If the conversion results in a pointer on a different 
            /// FixedDocument then we return one end of the FixedDocument (based on direction).
            ///  
            /// position in a DocumentSequence to convert
            /// direction of the desired conversion
            /// original pointer from FixedDocument
            private StaticTextPointer GetStaticPositionInChildContainer(StaticTextPointer textPosition, LogicalDirection direction, StaticTextPointer originalPosition) 
            {
                StaticTextPointer parentTextPointer = StaticTextPointer.Null; 
 
                if (!textPosition.IsNull)
                { 
                    DocumentSequenceTextPointer parentChangePosition = textPosition.CreateDynamicTextPointer(LogicalDirection.Forward) as DocumentSequenceTextPointer;
                    Debug.Assert(parentChangePosition != null);

                    // If the DocSequence position translates into a position in a different FixedDocument than 
                    // the original request, we return an end of the original FixedDocument (which end depends on direction)
                    ITextPointer childTp = parentChangePosition.ChildPointer; 
                    if (childTp.TextContainer != originalPosition.TextContainer) 
                    {
                        // If the position we started searching from is highlighted, cut the highlight 
                        // at the end of the text container.  Otherwise return null (the highlight must
                        // start in the next document).
                        if (IsContentHighlighted(originalPosition, direction))
                        { 
                            childTp = direction == LogicalDirection.Forward ?
                                                   originalPosition.TextContainer.End 
                                                 : originalPosition.TextContainer.Start; 
                            parentTextPointer = childTp.CreateStaticPointer();
                        } 
                        else
                        {
                            parentTextPointer = StaticTextPointer.Null;
                        } 
                    }
                    else 
                    { 
                        parentTextPointer = childTp.CreateStaticPointer();
                    } 
                }

                return parentTextPointer;
            } 
        }
 
        #endregion Private 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