TextContainerChangedEventArgs.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 / TextContainerChangedEventArgs.cs / 1305600 / TextContainerChangedEventArgs.cs

                            //---------------------------------------------------------------------------- 
//
// File: TextContainerChangedEventArgs.cs
//
// Description: The arguments sent when a TextChangedEvent is fired in a TextContainer. 
//
// History: 
//  8/3/2004 : [....] - Created - preparing for TextContainer eventing 
//             breaking change.
// 
//---------------------------------------------------------------------------

using System;
using System.Windows; 
using System.Windows.Controls;
using System.Collections.Generic; 
 
namespace System.Windows.Documents
{ 
    /// 
    ///  The TextContainerChangedEventArgs defines the event arguments sent when a
    ///  TextContainer is changed.
    ///  
    internal class TextContainerChangedEventArgs : EventArgs
    { 
        //----------------------------------------------------- 
        //
        //  Constructors 
        //
        //-----------------------------------------------------

        #region Constructors 

        internal TextContainerChangedEventArgs() 
        { 
            _changes = new SortedList();
        } 

        #endregion Constructors

        //------------------------------------------------------ 
        //
        //  Internal Methods 
        // 
        //-----------------------------------------------------
 
        #region Internal Methods

        // Called by TextElement when a local property value changes.
        // Sets the HasLocalPropertyValueChange property true. 
        internal void SetLocalPropertyValueChanged()
        { 
            _hasLocalPropertyValueChange = true; 
        }
 
        // Adds a new change to the Delta list.
        internal void AddChange(PrecursorTextChangeType textChange, int offset, int length, bool collectTextChanges)
        {
            if (textChange == PrecursorTextChangeType.ContentAdded || 
                textChange == PrecursorTextChangeType.ElementAdded ||
                textChange == PrecursorTextChangeType.ContentRemoved || 
                textChange == PrecursorTextChangeType.ElementExtracted) 
            {
                _hasContentAddedOrRemoved = true; 
            }

            if (!collectTextChanges)
            { 
                return;
            } 
 
            // We only want to add or remove the edges for empty elements (and ElementAdded /
            // ElementRemoved are only used with empty elements).  Note that since we're making 
            // two changes instead of one the order matters.  We have to treat the two cases differently.
            if (textChange == PrecursorTextChangeType.ElementAdded)
            {
                AddChangeToList(textChange, offset, 1); 
                AddChangeToList(textChange, offset + length - 1, 1);
            } 
            else if (textChange == PrecursorTextChangeType.ElementExtracted) 
            {
                AddChangeToList(textChange, offset + length - 1, 1); 
                AddChangeToList(textChange, offset, 1);
            }
            else if (textChange == PrecursorTextChangeType.PropertyModified)
            { 
                // ignore property changes
                return; 
            } 
            else
            { 
                AddChangeToList(textChange, offset, length);
            }
        }
 
#if PROPERTY_CHANGES
        // We don't merge property changes with each other when they're added, so that when an element is removed 
        // we can remove the property changes associated with it.  This method merges all of them at once. 
        internal void MergePropertyChanges()
        { 
            TextChange leftChange = null;
            for (int index = 0; index < Changes.Count; index++)
            {
                TextChange curChange = Changes.Values[index]; 
                if (leftChange == null || leftChange.Offset + leftChange.PropertyCount < curChange.Offset)
                { 
                    leftChange = curChange; 
                }
                else if (leftChange.Offset + leftChange.PropertyCount >= curChange.Offset) 
                {
                    if (MergePropertyChange(leftChange, curChange))
                    {
                        // Changes merged.  If the right-hand change is now empty, remove it. 
                        if (curChange.RemovedLength == 0 && curChange.AddedLength == 0)
                        { 
                            Changes.RemoveAt(index--); // decrement index since we're removing an element, otherwise we'll skip one 
                        }
                    } 
                }
            }
        }
#endif 

        #endregion Internal Methods 
 
        //------------------------------------------------------
        // 
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        ///  
        /// Returns true if at least one of the TextChangeSegments in this TextChangeCollection
        /// is not of type TextChange.LayoutAffected or TextChange.RenderAffect.  This means that 
        /// content was added or removed -- rather than content only affected by DependencyProperty
        /// values changes on scoping elements.
        /// 
        ///  
        internal bool HasContentAddedOrRemoved
        { 
            get 
            {
                return _hasContentAddedOrRemoved; 
            }
        }

        ///  
        /// Returns true if the collection contains one or more entries that
        /// result from a local property value changing on a TextElement. 
        ///  
        /// 
        /// Note "local property value" does NOT include property changes 
        /// that arrive via inheritance or styles.
        /// 
        internal bool HasLocalPropertyValueChange
        { 
            get
            { 
                return _hasLocalPropertyValueChange; 
            }
        } 

        /// 
        /// List of TextChanges representing the aggregate changes made to the container.
        ///  
        internal SortedList Changes
        { 
            get 
            {
                return _changes; 
            }
        }

        #endregion Internal Properties 

        #region Private Methods 
 
        //-----------------------------------------------------
        // 
        //  Private Methods
        //
        //------------------------------------------------------
 
        //
        // First we discover where the new change goes in the sorted list.  If there's already 
        // a change there, merge the new one with the old one. 
        //
        // Next, if the new change is an insertion or deletion, see if the offset of the change 
        // is within a range covered by an insertion at a lower offset.  If so, merge the change at
        // the higher offset into the previous change and delete the change at the higher offset.
        //
        // Next, if the change is a deletion, see if we delete enough characters to run into a 
        // change at a higher offset.  If so, merge the change at the higher offset into this newer
        // change and delete the older, higher-offset change. 
        // 
        // Finally, update all offsets higher than the current change to reflect the number of
        // characters inserted or deleted. 
        //
        private void AddChangeToList(PrecursorTextChangeType textChange, int offset, int length)
        {
            int offsetDelta = 0; // Value by which to adjust other offsets in list 
            int curIndex;        // loop counter
            TextChange change = null; // initialize to satisfy compiler 
            bool isDeletion = false; 
#if PROPERTY_CHANGES
            bool shiftPropertyChanges = false; 
#endif

            //
            // Is there already a change at this offset in our list?  If so, use it 
            //
            int keyIndex = Changes.IndexOfKey(offset); 
            if (keyIndex != -1) 
            {
                change = Changes.Values[keyIndex]; 
            }
            else
            {
                change = new TextChange(); 
                change.Offset = offset;
                Changes.Add(offset, change); 
                keyIndex = Changes.IndexOfKey(offset); 
            }
 
            //
            // Merge values from new change into empty or pre-existing change
            //
            if (textChange == PrecursorTextChangeType.ContentAdded || textChange == PrecursorTextChangeType.ElementAdded) 
            {
                change.AddedLength += length; 
                offsetDelta = length; 
#if PROPERTY_CHANGES
                if (change.PropertyCount > 0) 
                {
                    shiftPropertyChanges = true;
                }
#endif 
            }
            else if (textChange == PrecursorTextChangeType.ContentRemoved || textChange == PrecursorTextChangeType.ElementExtracted) 
            { 
                // Any deletions made after an addition just cancel out the earlier addition
                change.RemovedLength += Math.Max(0, length - change.AddedLength); 
                change.AddedLength = Math.Max(0, change.AddedLength - length);

#if PROPERTY_CHANGES
                // If an element was extracted, any property change made on that element should be removed as well 
                if (textChange == PrecursorTextChangeType.ElementExtracted)
                { 
                    change.SetPropertyCount(0); 
                }
                else 
                {
                    change.SetPropertyCount(Math.Max(0, change.PropertyCount - length));
                }
#endif 

                offsetDelta = -length; 
                isDeletion = true; 
            }
#if PROPERTY_CHANGES 
            else
            {
                // Property change
                if (change.PropertyCount < length) 
                {
                    change.SetPropertyCount(length); 
                } 
            }
#endif 

            //
            // Done with simple case.  Now look for changes that intersect.
            // There are two possible (non-exclusive) merge conditions: 
            //   1. A positionally preceding change spans this offset (this change is inserting
            //        into or deleting from the middle or right edge of previous inserted text) 
            //   2. On deletion, the change spans one or more positionally later changes 
            //
            if (keyIndex > 0 && textChange != PrecursorTextChangeType.PropertyModified) 
            {
                curIndex = keyIndex - 1;
                // Since we don't merge property changes, there could be an arbitrary number of
                // them between the new change and an overlapping insertion or overlapping property 
                // change.  In fact, there could be multiple property changes that overlap this
                // change.  We need to adjust ALL of them.  There can be only one overlapping 
                // insertion, but there's no way to leverage that fact. 
                TextChange mergedChange = null;
                while (curIndex >= 0) 
                {
                    TextChange prevChange = Changes.Values[curIndex];
#if PROPERTY_CHANGES
                    if (prevChange.Offset + prevChange.AddedLength >= offset || prevChange.Offset + prevChange.PropertyCount >= offset) 
#else
                    if (prevChange.Offset + prevChange.AddedLength >= offset) 
#endif 
                    {
                        if (MergeTextChangeLeft(prevChange, change, isDeletion, length)) 
                        {
                            mergedChange = prevChange;
                        }
                    } 
                    curIndex--;
                } 
                if (mergedChange != null) 
                {
                    // the change got merged.  Use the merged change as the basis for righthand merging. 
                    change = mergedChange;
                }
                // changes may have been deleted, so update the index of the change we're working with
                keyIndex = Changes.IndexOfKey(change.Offset); 
            }
 
            curIndex = keyIndex + 1; 
            if (isDeletion && curIndex < Changes.Count)
            { 
                while (curIndex < Changes.Count && Changes.Values[curIndex].Offset <= offset + length)
                {
                    // offset and length must be passed because if we've also merged left, we haven't yet adjusted indices.
                    // Note that MergeTextChangeRight() always removes Changes.Values[curIndex] from the list, so 
                    // we don't need to increment curIndex.
                    MergeTextChangeRight(Changes.Values[curIndex], change, offset, length); 
                } 
                // changes may have been deleted, so update the index of the change we're working with
                keyIndex = Changes.IndexOfKey(change.Offset); 
            }

            // Update all changes to reflect new offsets.
            // If offsetDelta is positive, go right to left; otherwise, go left to right. 
            // The order of the offsets will never change, so we can use an indexer safely.
            // To avoid nasty N-squared perf, create a new list instead of moving things within 
            // the old one. 
            //
            if (offsetDelta != 0) 
            {
                SortedList newChanges = new SortedList(Changes.Count);
                for (curIndex = 0; curIndex < Changes.Count; curIndex++)
                { 
                    TextChange curChange = Changes.Values[curIndex];
                    if (curIndex > keyIndex) 
                    { 
                        curChange.Offset += offsetDelta;
                    } 
                    newChanges.Add(curChange.Offset, curChange);
                }
                _changes = newChanges;
 
            }
 
            DeleteChangeIfEmpty(change); 

#if PROPERTY_CHANGES 
            // Finally, if the change was an insertion and there are property changes starting
            // at this location, the insertion is not part of the property changes.  Shift the
            // property changes forward by the length of the insertion.
            if (shiftPropertyChanges) 
            {
                int propertyCount = change.PropertyCount; 
                change.SetPropertyCount(0); 
                AddChangeToList(PrecursorTextChangeType.PropertyModified, offset + offsetDelta, propertyCount);
            } 
#endif
        }

        private void DeleteChangeIfEmpty(TextChange change) 
        {
#if PROPERTY_CHANGES 
            if (change.AddedLength == 0 && change.RemovedLength == 0 && change.PropertyCount == 0) 
#else
            if (change.AddedLength == 0 && change.RemovedLength == 0) 
#endif
            {
                Changes.Remove(change.Offset);
            } 
        }
 
        // returns true if the change merged into an earlier insertion 
        private bool MergeTextChangeLeft(TextChange oldChange, TextChange newChange, bool isDeletion, int length)
        { 
            // newChange is inserting or deleting text inside oldChange.
            int overlap;

#if PROPERTY_CHANGES 
            // Check for a property change in the old change that overlaps the new change
            if (oldChange.Offset + oldChange.PropertyCount >= newChange.Offset) 
            { 
                if (isDeletion)
                { 
                    overlap = oldChange.PropertyCount - (newChange.Offset - oldChange.Offset);
                    oldChange.SetPropertyCount(oldChange.PropertyCount - Math.Min(overlap, length));
                    DeleteChangeIfEmpty(oldChange);
                } 
                else
                { 
                    oldChange.SetPropertyCount(oldChange.PropertyCount + length); 
                }
            } 
#endif

            if (oldChange.Offset + oldChange.AddedLength >= newChange.Offset)
            { 
                // If any text was deleted in the new change, adjust the added count of the
                // previous change accordingly.  The removed count of the new change must be 
                // adjusted by the same amount. 
                if (isDeletion)
                { 
                    overlap = oldChange.AddedLength - (newChange.Offset - oldChange.Offset);
                    int cancelledCount = Math.Min(overlap, newChange.RemovedLength);
                    oldChange.AddedLength -= cancelledCount;
                    oldChange.RemovedLength += (length - cancelledCount); 
                }
                else 
                { 
                    oldChange.AddedLength += length;
                } 
#if PROPERTY_CHANGES
                if (newChange.PropertyCount == 0)
                {
                    // We've merged the data from the new change into an older one, so we can 
                    // delete the change from the list.
                    Changes.Remove(newChange.Offset); 
                } 
                else
                { 
                    // Can't delete the change, since there's pre-existing property change data, so
                    // just clear the data instead.
                    newChange.AddedLength = 0;
                    newChange.RemovedLength = 0; 
                }
#else 
                // We've merged the data from the new change into an older one, so we can 
                // delete the change from the list.
                Changes.Remove(newChange.Offset); 
#endif
                return true;
            }
            return false; 
        }
 
        private void MergeTextChangeRight(TextChange oldChange, TextChange newChange, int offset, int length) 
        {
            // If the old change is an addition, find the length of the overlap 
            // and adjust the addition and new deletion counts accordingly
            int addedLengthOverlap = oldChange.AddedLength > 0 ? offset + length - oldChange.Offset : 0;

#if PROPERTY_CHANGES 
            // Check for a property change in the new change that overlaps the old change
            int propertyOverlap = newChange.Offset + newChange.PropertyCount - oldChange.Offset; 
            if (propertyOverlap > 0) 
            {
                int delta = Math.Min(propertyOverlap, addedLengthOverlap); 
                newChange.SetPropertyCount(newChange.PropertyCount - delta);
                // Don't need to adjust oldChange.PropertyCount, since oldChange is about to be removed
            }
#endif 

            // adjust removed count 
            if (addedLengthOverlap >= oldChange.AddedLength) 
            {
                // old change is entirely within new one 
                newChange.RemovedLength += (oldChange.RemovedLength - oldChange.AddedLength);
                Changes.Remove(oldChange.Offset);
            }
            else 
            {
                newChange.RemovedLength += (oldChange.RemovedLength - addedLengthOverlap); 
                newChange.AddedLength += (oldChange.AddedLength - addedLengthOverlap); 
                Changes.Remove(oldChange.Offset);
            } 
        }

#if PROPERTY_CHANGES
        private bool MergePropertyChange(TextChange leftChange, TextChange rightChange) 
        {
            if (leftChange.Offset + leftChange.PropertyCount >= rightChange.Offset) 
            { 
                if (leftChange.Offset + leftChange.PropertyCount < rightChange.Offset + rightChange.PropertyCount)
                { 
                    // right change is partially inside left, but not completely.
                    int overlap = leftChange.Offset + leftChange.PropertyCount - rightChange.Offset;
                    int delta = rightChange.PropertyCount - overlap;
                    leftChange.SetPropertyCount(leftChange.PropertyCount + delta); 
                }
                rightChange.SetPropertyCount(0); 
                return true; 
            }
            return false; 
        }
#endif

        #endregion Private Methods 

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

        #region Private Fields
 
        // true if at least one of the changes in this TextChangeCollection
        // is not of type TextChange.LayoutAffected or TextChange.RenderAffect. 
        private bool _hasContentAddedOrRemoved; 

        // True if the collection contains one or more entries that 
        // result from a local property value changing on a TextElement.
        private bool _hasLocalPropertyValueChange;

        private SortedList _changes; 

        #endregion Private Fields 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// File: TextContainerChangedEventArgs.cs
//
// Description: The arguments sent when a TextChangedEvent is fired in a TextContainer. 
//
// History: 
//  8/3/2004 : [....] - Created - preparing for TextContainer eventing 
//             breaking change.
// 
//---------------------------------------------------------------------------

using System;
using System.Windows; 
using System.Windows.Controls;
using System.Collections.Generic; 
 
namespace System.Windows.Documents
{ 
    /// 
    ///  The TextContainerChangedEventArgs defines the event arguments sent when a
    ///  TextContainer is changed.
    ///  
    internal class TextContainerChangedEventArgs : EventArgs
    { 
        //----------------------------------------------------- 
        //
        //  Constructors 
        //
        //-----------------------------------------------------

        #region Constructors 

        internal TextContainerChangedEventArgs() 
        { 
            _changes = new SortedList();
        } 

        #endregion Constructors

        //------------------------------------------------------ 
        //
        //  Internal Methods 
        // 
        //-----------------------------------------------------
 
        #region Internal Methods

        // Called by TextElement when a local property value changes.
        // Sets the HasLocalPropertyValueChange property true. 
        internal void SetLocalPropertyValueChanged()
        { 
            _hasLocalPropertyValueChange = true; 
        }
 
        // Adds a new change to the Delta list.
        internal void AddChange(PrecursorTextChangeType textChange, int offset, int length, bool collectTextChanges)
        {
            if (textChange == PrecursorTextChangeType.ContentAdded || 
                textChange == PrecursorTextChangeType.ElementAdded ||
                textChange == PrecursorTextChangeType.ContentRemoved || 
                textChange == PrecursorTextChangeType.ElementExtracted) 
            {
                _hasContentAddedOrRemoved = true; 
            }

            if (!collectTextChanges)
            { 
                return;
            } 
 
            // We only want to add or remove the edges for empty elements (and ElementAdded /
            // ElementRemoved are only used with empty elements).  Note that since we're making 
            // two changes instead of one the order matters.  We have to treat the two cases differently.
            if (textChange == PrecursorTextChangeType.ElementAdded)
            {
                AddChangeToList(textChange, offset, 1); 
                AddChangeToList(textChange, offset + length - 1, 1);
            } 
            else if (textChange == PrecursorTextChangeType.ElementExtracted) 
            {
                AddChangeToList(textChange, offset + length - 1, 1); 
                AddChangeToList(textChange, offset, 1);
            }
            else if (textChange == PrecursorTextChangeType.PropertyModified)
            { 
                // ignore property changes
                return; 
            } 
            else
            { 
                AddChangeToList(textChange, offset, length);
            }
        }
 
#if PROPERTY_CHANGES
        // We don't merge property changes with each other when they're added, so that when an element is removed 
        // we can remove the property changes associated with it.  This method merges all of them at once. 
        internal void MergePropertyChanges()
        { 
            TextChange leftChange = null;
            for (int index = 0; index < Changes.Count; index++)
            {
                TextChange curChange = Changes.Values[index]; 
                if (leftChange == null || leftChange.Offset + leftChange.PropertyCount < curChange.Offset)
                { 
                    leftChange = curChange; 
                }
                else if (leftChange.Offset + leftChange.PropertyCount >= curChange.Offset) 
                {
                    if (MergePropertyChange(leftChange, curChange))
                    {
                        // Changes merged.  If the right-hand change is now empty, remove it. 
                        if (curChange.RemovedLength == 0 && curChange.AddedLength == 0)
                        { 
                            Changes.RemoveAt(index--); // decrement index since we're removing an element, otherwise we'll skip one 
                        }
                    } 
                }
            }
        }
#endif 

        #endregion Internal Methods 
 
        //------------------------------------------------------
        // 
        //  Internal Properties
        //
        //------------------------------------------------------
 
        #region Internal Properties
 
        ///  
        /// Returns true if at least one of the TextChangeSegments in this TextChangeCollection
        /// is not of type TextChange.LayoutAffected or TextChange.RenderAffect.  This means that 
        /// content was added or removed -- rather than content only affected by DependencyProperty
        /// values changes on scoping elements.
        /// 
        ///  
        internal bool HasContentAddedOrRemoved
        { 
            get 
            {
                return _hasContentAddedOrRemoved; 
            }
        }

        ///  
        /// Returns true if the collection contains one or more entries that
        /// result from a local property value changing on a TextElement. 
        ///  
        /// 
        /// Note "local property value" does NOT include property changes 
        /// that arrive via inheritance or styles.
        /// 
        internal bool HasLocalPropertyValueChange
        { 
            get
            { 
                return _hasLocalPropertyValueChange; 
            }
        } 

        /// 
        /// List of TextChanges representing the aggregate changes made to the container.
        ///  
        internal SortedList Changes
        { 
            get 
            {
                return _changes; 
            }
        }

        #endregion Internal Properties 

        #region Private Methods 
 
        //-----------------------------------------------------
        // 
        //  Private Methods
        //
        //------------------------------------------------------
 
        //
        // First we discover where the new change goes in the sorted list.  If there's already 
        // a change there, merge the new one with the old one. 
        //
        // Next, if the new change is an insertion or deletion, see if the offset of the change 
        // is within a range covered by an insertion at a lower offset.  If so, merge the change at
        // the higher offset into the previous change and delete the change at the higher offset.
        //
        // Next, if the change is a deletion, see if we delete enough characters to run into a 
        // change at a higher offset.  If so, merge the change at the higher offset into this newer
        // change and delete the older, higher-offset change. 
        // 
        // Finally, update all offsets higher than the current change to reflect the number of
        // characters inserted or deleted. 
        //
        private void AddChangeToList(PrecursorTextChangeType textChange, int offset, int length)
        {
            int offsetDelta = 0; // Value by which to adjust other offsets in list 
            int curIndex;        // loop counter
            TextChange change = null; // initialize to satisfy compiler 
            bool isDeletion = false; 
#if PROPERTY_CHANGES
            bool shiftPropertyChanges = false; 
#endif

            //
            // Is there already a change at this offset in our list?  If so, use it 
            //
            int keyIndex = Changes.IndexOfKey(offset); 
            if (keyIndex != -1) 
            {
                change = Changes.Values[keyIndex]; 
            }
            else
            {
                change = new TextChange(); 
                change.Offset = offset;
                Changes.Add(offset, change); 
                keyIndex = Changes.IndexOfKey(offset); 
            }
 
            //
            // Merge values from new change into empty or pre-existing change
            //
            if (textChange == PrecursorTextChangeType.ContentAdded || textChange == PrecursorTextChangeType.ElementAdded) 
            {
                change.AddedLength += length; 
                offsetDelta = length; 
#if PROPERTY_CHANGES
                if (change.PropertyCount > 0) 
                {
                    shiftPropertyChanges = true;
                }
#endif 
            }
            else if (textChange == PrecursorTextChangeType.ContentRemoved || textChange == PrecursorTextChangeType.ElementExtracted) 
            { 
                // Any deletions made after an addition just cancel out the earlier addition
                change.RemovedLength += Math.Max(0, length - change.AddedLength); 
                change.AddedLength = Math.Max(0, change.AddedLength - length);

#if PROPERTY_CHANGES
                // If an element was extracted, any property change made on that element should be removed as well 
                if (textChange == PrecursorTextChangeType.ElementExtracted)
                { 
                    change.SetPropertyCount(0); 
                }
                else 
                {
                    change.SetPropertyCount(Math.Max(0, change.PropertyCount - length));
                }
#endif 

                offsetDelta = -length; 
                isDeletion = true; 
            }
#if PROPERTY_CHANGES 
            else
            {
                // Property change
                if (change.PropertyCount < length) 
                {
                    change.SetPropertyCount(length); 
                } 
            }
#endif 

            //
            // Done with simple case.  Now look for changes that intersect.
            // There are two possible (non-exclusive) merge conditions: 
            //   1. A positionally preceding change spans this offset (this change is inserting
            //        into or deleting from the middle or right edge of previous inserted text) 
            //   2. On deletion, the change spans one or more positionally later changes 
            //
            if (keyIndex > 0 && textChange != PrecursorTextChangeType.PropertyModified) 
            {
                curIndex = keyIndex - 1;
                // Since we don't merge property changes, there could be an arbitrary number of
                // them between the new change and an overlapping insertion or overlapping property 
                // change.  In fact, there could be multiple property changes that overlap this
                // change.  We need to adjust ALL of them.  There can be only one overlapping 
                // insertion, but there's no way to leverage that fact. 
                TextChange mergedChange = null;
                while (curIndex >= 0) 
                {
                    TextChange prevChange = Changes.Values[curIndex];
#if PROPERTY_CHANGES
                    if (prevChange.Offset + prevChange.AddedLength >= offset || prevChange.Offset + prevChange.PropertyCount >= offset) 
#else
                    if (prevChange.Offset + prevChange.AddedLength >= offset) 
#endif 
                    {
                        if (MergeTextChangeLeft(prevChange, change, isDeletion, length)) 
                        {
                            mergedChange = prevChange;
                        }
                    } 
                    curIndex--;
                } 
                if (mergedChange != null) 
                {
                    // the change got merged.  Use the merged change as the basis for righthand merging. 
                    change = mergedChange;
                }
                // changes may have been deleted, so update the index of the change we're working with
                keyIndex = Changes.IndexOfKey(change.Offset); 
            }
 
            curIndex = keyIndex + 1; 
            if (isDeletion && curIndex < Changes.Count)
            { 
                while (curIndex < Changes.Count && Changes.Values[curIndex].Offset <= offset + length)
                {
                    // offset and length must be passed because if we've also merged left, we haven't yet adjusted indices.
                    // Note that MergeTextChangeRight() always removes Changes.Values[curIndex] from the list, so 
                    // we don't need to increment curIndex.
                    MergeTextChangeRight(Changes.Values[curIndex], change, offset, length); 
                } 
                // changes may have been deleted, so update the index of the change we're working with
                keyIndex = Changes.IndexOfKey(change.Offset); 
            }

            // Update all changes to reflect new offsets.
            // If offsetDelta is positive, go right to left; otherwise, go left to right. 
            // The order of the offsets will never change, so we can use an indexer safely.
            // To avoid nasty N-squared perf, create a new list instead of moving things within 
            // the old one. 
            //
            if (offsetDelta != 0) 
            {
                SortedList newChanges = new SortedList(Changes.Count);
                for (curIndex = 0; curIndex < Changes.Count; curIndex++)
                { 
                    TextChange curChange = Changes.Values[curIndex];
                    if (curIndex > keyIndex) 
                    { 
                        curChange.Offset += offsetDelta;
                    } 
                    newChanges.Add(curChange.Offset, curChange);
                }
                _changes = newChanges;
 
            }
 
            DeleteChangeIfEmpty(change); 

#if PROPERTY_CHANGES 
            // Finally, if the change was an insertion and there are property changes starting
            // at this location, the insertion is not part of the property changes.  Shift the
            // property changes forward by the length of the insertion.
            if (shiftPropertyChanges) 
            {
                int propertyCount = change.PropertyCount; 
                change.SetPropertyCount(0); 
                AddChangeToList(PrecursorTextChangeType.PropertyModified, offset + offsetDelta, propertyCount);
            } 
#endif
        }

        private void DeleteChangeIfEmpty(TextChange change) 
        {
#if PROPERTY_CHANGES 
            if (change.AddedLength == 0 && change.RemovedLength == 0 && change.PropertyCount == 0) 
#else
            if (change.AddedLength == 0 && change.RemovedLength == 0) 
#endif
            {
                Changes.Remove(change.Offset);
            } 
        }
 
        // returns true if the change merged into an earlier insertion 
        private bool MergeTextChangeLeft(TextChange oldChange, TextChange newChange, bool isDeletion, int length)
        { 
            // newChange is inserting or deleting text inside oldChange.
            int overlap;

#if PROPERTY_CHANGES 
            // Check for a property change in the old change that overlaps the new change
            if (oldChange.Offset + oldChange.PropertyCount >= newChange.Offset) 
            { 
                if (isDeletion)
                { 
                    overlap = oldChange.PropertyCount - (newChange.Offset - oldChange.Offset);
                    oldChange.SetPropertyCount(oldChange.PropertyCount - Math.Min(overlap, length));
                    DeleteChangeIfEmpty(oldChange);
                } 
                else
                { 
                    oldChange.SetPropertyCount(oldChange.PropertyCount + length); 
                }
            } 
#endif

            if (oldChange.Offset + oldChange.AddedLength >= newChange.Offset)
            { 
                // If any text was deleted in the new change, adjust the added count of the
                // previous change accordingly.  The removed count of the new change must be 
                // adjusted by the same amount. 
                if (isDeletion)
                { 
                    overlap = oldChange.AddedLength - (newChange.Offset - oldChange.Offset);
                    int cancelledCount = Math.Min(overlap, newChange.RemovedLength);
                    oldChange.AddedLength -= cancelledCount;
                    oldChange.RemovedLength += (length - cancelledCount); 
                }
                else 
                { 
                    oldChange.AddedLength += length;
                } 
#if PROPERTY_CHANGES
                if (newChange.PropertyCount == 0)
                {
                    // We've merged the data from the new change into an older one, so we can 
                    // delete the change from the list.
                    Changes.Remove(newChange.Offset); 
                } 
                else
                { 
                    // Can't delete the change, since there's pre-existing property change data, so
                    // just clear the data instead.
                    newChange.AddedLength = 0;
                    newChange.RemovedLength = 0; 
                }
#else 
                // We've merged the data from the new change into an older one, so we can 
                // delete the change from the list.
                Changes.Remove(newChange.Offset); 
#endif
                return true;
            }
            return false; 
        }
 
        private void MergeTextChangeRight(TextChange oldChange, TextChange newChange, int offset, int length) 
        {
            // If the old change is an addition, find the length of the overlap 
            // and adjust the addition and new deletion counts accordingly
            int addedLengthOverlap = oldChange.AddedLength > 0 ? offset + length - oldChange.Offset : 0;

#if PROPERTY_CHANGES 
            // Check for a property change in the new change that overlaps the old change
            int propertyOverlap = newChange.Offset + newChange.PropertyCount - oldChange.Offset; 
            if (propertyOverlap > 0) 
            {
                int delta = Math.Min(propertyOverlap, addedLengthOverlap); 
                newChange.SetPropertyCount(newChange.PropertyCount - delta);
                // Don't need to adjust oldChange.PropertyCount, since oldChange is about to be removed
            }
#endif 

            // adjust removed count 
            if (addedLengthOverlap >= oldChange.AddedLength) 
            {
                // old change is entirely within new one 
                newChange.RemovedLength += (oldChange.RemovedLength - oldChange.AddedLength);
                Changes.Remove(oldChange.Offset);
            }
            else 
            {
                newChange.RemovedLength += (oldChange.RemovedLength - addedLengthOverlap); 
                newChange.AddedLength += (oldChange.AddedLength - addedLengthOverlap); 
                Changes.Remove(oldChange.Offset);
            } 
        }

#if PROPERTY_CHANGES
        private bool MergePropertyChange(TextChange leftChange, TextChange rightChange) 
        {
            if (leftChange.Offset + leftChange.PropertyCount >= rightChange.Offset) 
            { 
                if (leftChange.Offset + leftChange.PropertyCount < rightChange.Offset + rightChange.PropertyCount)
                { 
                    // right change is partially inside left, but not completely.
                    int overlap = leftChange.Offset + leftChange.PropertyCount - rightChange.Offset;
                    int delta = rightChange.PropertyCount - overlap;
                    leftChange.SetPropertyCount(leftChange.PropertyCount + delta); 
                }
                rightChange.SetPropertyCount(0); 
                return true; 
            }
            return false; 
        }
#endif

        #endregion Private Methods 

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

        #region Private Fields
 
        // true if at least one of the changes in this TextChangeCollection
        // is not of type TextChange.LayoutAffected or TextChange.RenderAffect. 
        private bool _hasContentAddedOrRemoved; 

        // True if the collection contains one or more entries that 
        // result from a local property value changing on a TextElement.
        private bool _hasLocalPropertyValueChange;

        private SortedList _changes; 

        #endregion Private Fields 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

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