Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Documents / documentsequencetextpointer.cs / 1 / documentsequencetextpointer.cs
//---------------------------------------------------------------------------- //// Copyright (C) 2004 by Microsoft Corporation. All rights reserved. // // // Description: // DocumentSequenceTextPointer is an implementation of ITextPointer/ITextPointer // for FixedDocumentSequence. It is the base class for DocumentSequenceTextPointer and // DocumentSequenceTextPointer. // // History: // 07/12/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.Documents; using MS.Utility; using MS.Internal; using System.Windows; using System; using System.Diagnostics; ////// DocumentSequenceTextPointer is an implementation of ITextPointer for FixedDocumentSequence /// internal sealed class DocumentSequenceTextPointer : ContentPosition, ITextPointer { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // Ctor always set mutable flag to false internal DocumentSequenceTextPointer(ChildDocumentBlock childBlock, ITextPointer childPosition) { Debug.Assert(childBlock != null); Debug.Assert(childPosition != null); _childBlock = childBlock; _childTp = childPosition; } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region TextPointer Methods ////// void ITextPointer.SetLogicalDirection(LogicalDirection direction) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); _childTp.SetLogicalDirection(direction); } ////// /// int ITextPointer.CompareTo(ITextPointer position) { return DocumentSequenceTextPointer.CompareTo(this, position); } int ITextPointer.CompareTo(StaticTextPointer position) { return ((ITextPointer)this).CompareTo((ITextPointer)position.Handle0); } ////// /// int ITextPointer.GetOffsetToPosition(ITextPointer position) { return DocumentSequenceTextPointer.GetOffsetToPosition(this, position); } ////// /// TextPointerContext ITextPointer.GetPointerContext(LogicalDirection direction) { return DocumentSequenceTextPointer.GetPointerContext(this, direction); } ////// /// ////// Return 0 if non-text run int ITextPointer.GetTextRunLength(LogicalDirection direction) { return DocumentSequenceTextPointer.GetTextRunLength(this, direction); } //string ITextPointer.GetTextInRun(LogicalDirection direction) { return TextPointerBase.GetTextInRun(this, direction); } /// /// ////// Only reutrn uninterrupted runs of text int ITextPointer.GetTextInRun(LogicalDirection direction, char[] textBuffer, int startIndex, int count) { return DocumentSequenceTextPointer.GetTextInRun(this, direction, textBuffer, startIndex, count); } ////// ////// Return null if the embedded object does not exist object ITextPointer.GetAdjacentElement(LogicalDirection direction) { return DocumentSequenceTextPointer.GetAdjacentElement(this, direction); } ////// ////// Return null if no TextElement in the direction Type ITextPointer.GetElementType(LogicalDirection direction) { return DocumentSequenceTextPointer.GetElementType(this, direction); } ////// bool ITextPointer.HasEqualScope(ITextPointer position) { return DocumentSequenceTextPointer.HasEqualScope(this, position); } ////// /// ////// return property values even if there is no scoping element object ITextPointer.GetValue(DependencyProperty property) { return DocumentSequenceTextPointer.GetValue(this, property); } ////// ////// Throws InvalidOperationException if there is no scoping element object ITextPointer.ReadLocalValue(DependencyProperty property) { return DocumentSequenceTextPointer.ReadLocalValue(this, property); } ////// ////// Returns an empty enumerator if there is no scoping element LocalValueEnumerator ITextPointer.GetLocalValueEnumerator() { return DocumentSequenceTextPointer.GetLocalValueEnumerator(this); } ITextPointer ITextPointer.CreatePointer() { return DocumentSequenceTextPointer.CreatePointer(this); } // Unoptimized CreateStaticPointer implementation. // Creates a simple wrapper for an ITextPointer instance. StaticTextPointer ITextPointer.CreateStaticPointer() { return new StaticTextPointer(((ITextPointer)this).TextContainer, ((ITextPointer)this).CreatePointer()); } ITextPointer ITextPointer.CreatePointer(int distance) { return DocumentSequenceTextPointer.CreatePointer(this, distance); } ITextPointer ITextPointer.CreatePointer(LogicalDirection gravity) { return DocumentSequenceTextPointer.CreatePointer(this, gravity); } ////// ITextPointer ITextPointer.CreatePointer(int distance, LogicalDirection gravity) { return DocumentSequenceTextPointer.CreatePointer(this, distance, gravity); } ///// void ITextPointer.Freeze() { _isFrozen = true; } /// /// ITextPointer ITextPointer.GetFrozenPointer(LogicalDirection logicalDirection) { return TextPointerBase.GetFrozenPointer(this, logicalDirection); } ////// /// Inserts text at a specified position. /// /// /// Text to insert. /// void ITextPointer.InsertTextInRun(string textData) { throw new InvalidOperationException(SR.Get(SRID.DocumentReadOnly)); } ////// Removes content covered by a pair of positions. /// /// /// Position following the last symbol to delete. endPosition must be /// scoped by the same text element as startPosition. /// void ITextPointer.DeleteContentToPosition(ITextPointer limit) { throw new InvalidOperationException(SR.Get(SRID.DocumentReadOnly)); } // Candidate for replacing MoveToNextContextPosition for immutable TextPointer model ITextPointer ITextPointer.GetNextContextPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); if (pointer.MoveToNextContextPosition(direction)) { pointer.Freeze(); } else { pointer = null; } return pointer; } // Candidate for replacing MoveToInsertionPosition for immutable TextPointer model ITextPointer ITextPointer.GetInsertionPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); pointer.MoveToInsertionPosition(direction); pointer.Freeze(); return pointer; } // Returns the closest insertion position, treating all unicode code points // as valid insertion positions. A useful performance win over // GetNextInsertionPosition when only formatting scopes are important. ITextPointer ITextPointer.GetFormatNormalizedPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); TextPointerBase.MoveToFormatNormalizedPosition(pointer, direction); pointer.Freeze(); return pointer; } // Candidate for replacing MoveToNextInsertionPosition for immutable TextPointer model ITextPointer ITextPointer.GetNextInsertionPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); if (pointer.MoveToNextInsertionPosition(direction)) { pointer.Freeze(); } else { pointer = null; } return pointer; } ///bool ITextPointer.ValidateLayout() { return TextPointerBase.ValidateLayout(this, ((ITextPointer)this).TextContainer.TextView); } #endregion TextPointer Methods #region Public Methods #if DEBUG /// /// Debug only ToString override. /// public override string ToString() { return DocumentSequenceTextPointer.ToString(this); } #endif // DEBUG #endregion Public Methods //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ #region TextPointer Properties //Type ITextPointer.ParentType { get { return DocumentSequenceTextPointer.GetElementType(this); } } /// /// ITextContainer ITextPointer.TextContainer { get { return this.AggregatedContainer; } } ///// bool ITextPointer.HasValidLayout { get { return (((ITextPointer)this).TextContainer.TextView != null && ((ITextPointer)this).TextContainer.TextView.IsValid && ((ITextPointer)this).TextContainer.TextView.Contains(this)); } } // bool ITextPointer.IsAtCaretUnitBoundary { get { Invariant.Assert(((ITextPointer)this).HasValidLayout); ITextView textView = ((ITextPointer)this).TextContainer.TextView; bool isAtCaretUnitBoundary = textView.IsAtCaretUnitBoundary(this); if (!isAtCaretUnitBoundary && ((ITextPointer)this).LogicalDirection == LogicalDirection.Backward) { // In MIL Text and TextView worlds, a position at trailing edge of a newline (with backward gravity) // is not an allowed caret stop. // However, in TextPointer world we must allow such a position to be a valid insertion position, // since it breaks textrange normalization for empty ranges. // Hence, we need to check for TextView.IsAtCaretUnitBoundary in reverse direction below. ITextPointer positionForwardGravity = ((ITextPointer)this).CreatePointer(LogicalDirection.Forward); isAtCaretUnitBoundary = textView.IsAtCaretUnitBoundary(positionForwardGravity); } return isAtCaretUnitBoundary; } } /// /// LogicalDirection ITextPointer.LogicalDirection { get { return _childTp.LogicalDirection; } } bool ITextPointer.IsAtInsertionPosition { get { return TextPointerBase.IsAtInsertionPosition(this); } } ///// bool ITextPointer.IsFrozen { get { return _isFrozen; } } // int ITextPointer.Offset { get { return TextPointerBase.GetOffset(this); } } // Not implemented. int ITextPointer.CharOffset { get { #pragma warning suppress 56503 throw new NotImplementedException(); } } #endregion TextPointer Properties //----------------------------------------------------- // // Public Events // //------------------------------------------------------ //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region TextNavigator Methods /// /// bool ITextPointer.MoveToNextContextPosition(LogicalDirection direction) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); return DocumentSequenceTextPointer.iScan(this, direction); } ////// /// int ITextPointer.MoveByOffset(int offset) { if (_isFrozen) throw new InvalidOperationException(SR.Get(SRID.TextPositionIsFrozen)); if (DocumentSequenceTextPointer.iScan(this, offset)) { return offset; } else { return 0; } } ////// /// void ITextPointer.MoveToPosition(ITextPointer position) { DocumentSequenceTextPointer tp = this.AggregatedContainer.VerifyPosition(position); Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); LogicalDirection gravity = this.ChildPointer.LogicalDirection; this.ChildBlock = tp.ChildBlock; if (this.ChildPointer.TextContainer == tp.ChildPointer.TextContainer) { this.ChildPointer.MoveToPosition(tp.ChildPointer); } else { this.ChildPointer = tp.ChildPointer.CreatePointer(); this.ChildPointer.SetLogicalDirection(gravity); } } ////// /// void ITextPointer.MoveToElementEdge(ElementEdge edge) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); this.ChildPointer.MoveToElementEdge(edge); } ///// int ITextPointer.MoveToLineBoundary(int count) { return TextPointerBase.MoveToLineBoundary(this, ((ITextPointer)this).TextContainer.TextView, count, true); } // Rect ITextPointer.GetCharacterRect(LogicalDirection direction) { return TextPointerBase.GetCharacterRect(this, direction); } bool ITextPointer.MoveToInsertionPosition(LogicalDirection direction) { return TextPointerBase.MoveToInsertionPosition(this, direction); } bool ITextPointer.MoveToNextInsertionPosition(LogicalDirection direction) { return TextPointerBase.MoveToNextInsertionPosition(this, direction); } #endregion TextNavigator Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties internal DocumentSequenceTextContainer AggregatedContainer { get { return _childBlock.AggregatedContainer; } } // Accessor to base class ChildBlock. internal ChildDocumentBlock ChildBlock { get { return _childBlock; } set { _childBlock = value; } } // Helper exposing base classes ChildPointer as an ITextPointer, // which is guaranteed safe, since we set its value in the ctor // for this class. internal ITextPointer ChildPointer { get { return _childTp; } set { _childTp = value; Debug.Assert(_childTp != null); } } #if DEBUG // Debug-only identifier. private uint DebugId { get { return _debugId; } } #endif // DEBUG #endregion Internal Properties // ====================================================== // Static part of a class // // DocumentSequenceTextPointer is a static class that is provided all the // common functions of ITextPointer/ITextNavigaor for DocumentSequence. // Since we don't have multiple inheritance, this is a way to share code between // DocumentSequenceTextPointer and DocumentSequenceTextPointer. // //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region TextPointer Methods ////// public static int CompareTo(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); // Now do compare return xGapAwareCompareTo(thisTp, tp); } ////// /// public static int GetOffsetToPosition(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); int comp = xGapAwareCompareTo(thisTp, tp); if (comp == 0) { return 0; } else if (comp <= 0) { return xGapAwareGetDistance(thisTp, tp); } else { return -1 * xGapAwareGetDistance(tp, thisTp); } } ////// /// public static TextPointerContext GetPointerContext(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return xGapAwareGetSymbolType(thisTp, direction); } ////// /// ////// Return 0 if non-text run public static int GetTextRunLength(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return thisTp.ChildPointer.GetTextRunLength(direction); } ////// ////// Only reutrn uninterrupted runs of text public static int GetTextInRun(DocumentSequenceTextPointer thisTp, LogicalDirection direction, char[] textBuffer, int startIndex, int count) { ValidationHelper.VerifyDirection(direction, "direction"); if (textBuffer == null) { throw new ArgumentNullException("textBuffer"); } if (startIndex < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "startIndex")); } if (startIndex > textBuffer.Length) { throw new ArgumentException(SR.Get(SRID.StartIndexExceedsBufferSize, startIndex, textBuffer.Length)); } if (count < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "count")); } if (count > textBuffer.Length - startIndex) { throw new ArgumentException(SR.Get(SRID.MaxLengthExceedsBufferSize, count, textBuffer.Length, startIndex)); } return thisTp.ChildPointer.GetTextInRun(direction, textBuffer, startIndex, count); } ////// ////// Return null if the embedded object does not exist public static object GetAdjacentElement(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return xGapAwareGetEmbeddedElement(thisTp, direction); } ////// ////// Return null if no TextElement in the direction public static Type GetElementType(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetElementType(direction); } ////// public static Type GetElementType(DocumentSequenceTextPointer thisTp) { return thisTp.ChildPointer.ParentType; } ////// /// public static bool HasEqualScope(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); if (thisTp.ChildPointer.TextContainer == tp.ChildPointer.TextContainer) { return thisTp.ChildPointer.HasEqualScope(tp.ChildPointer); } // The TextOM speced behavior is if both scopes are null, return true. return thisTp.ChildPointer.ParentType == typeof(FixedDocument) && tp.ChildPointer.ParentType == typeof(FixedDocument); } ////// /// ////// return property values even if there is no scoping element public static object GetValue(DocumentSequenceTextPointer thisTp, DependencyProperty property) { if (property == null) { throw new ArgumentNullException("property"); } return thisTp.ChildPointer.GetValue(property); } ////// ////// Throws InvalidOperationException if there is no scoping element public static object ReadLocalValue(DocumentSequenceTextPointer thisTp, DependencyProperty property) { if (property == null) { throw new ArgumentNullException("property"); } return thisTp.ChildPointer.ReadLocalValue(property); } ////// ////// Returns an empty enumerator if there is no scoping element public static LocalValueEnumerator GetLocalValueEnumerator(DocumentSequenceTextPointer thisTp) { return thisTp.ChildPointer.GetLocalValueEnumerator(); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp) { return CreatePointer(thisTp, 0, thisTp.ChildPointer.LogicalDirection); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, int distance) { return CreatePointer(thisTp, distance, thisTp.ChildPointer.LogicalDirection); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, LogicalDirection gravity) { return CreatePointer(thisTp, 0, gravity); } ////// public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, int distance, LogicalDirection gravity) { ValidationHelper.VerifyDirection(gravity, "gravity"); // Special case for common case of distance == 0 // to avoid calculating child container size, which // could be expansive especially in case where child // container requires virtualization. DocumentSequenceTextPointer newTp = new DocumentSequenceTextPointer(thisTp.ChildBlock, thisTp.ChildPointer.CreatePointer(gravity)); if (distance != 0) { if (!xGapAwareScan(newTp, distance)) { throw new ArgumentException(SR.Get(SRID.BadDistance), "distance"); } } return newTp; } #endregion TextPointer Methods //----------------------------------------------------- // // Public Properties // //----------------------------------------------------- //----------------------------------------------------- // // Public Events // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Internal Method, input parameter contains TP that is not synced to generation internal static bool iScan(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { bool moved = thisTp.ChildPointer.MoveToNextContextPosition(direction); if (!moved) { moved = xGapAwareScan(thisTp, (direction == LogicalDirection.Forward ? 1 : -1)); } return moved; } // Internal Method, input parameter contains TP that is not synced to generation internal static bool iScan(DocumentSequenceTextPointer thisTp, int distance) { return xGapAwareScan(thisTp, distance); } #if DEBUG // Allocate a unique debug-only ID as identifier. internal static uint GetDebugId() { return _debugIdCounter++; } internal static string ToString(DocumentSequenceTextPointer thisTp) { return (thisTp is DocumentSequenceTextPointer ? "DSTP" : "DSTN") + " Id=" + thisTp.DebugId + " B=" + thisTp.ChildBlock.DebugId + " G=" + thisTp.ChildPointer.LogicalDirection ; } #endif #endregion Internal Methods //------------------------------------------------------ // // Internal Property // //----------------------------------------------------- //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext context = thisTp.ChildPointer.GetPointerContext(direction); if (context != TextPointerContext.None) { return thisTp; } ChildDocumentBlock block = thisTp.ChildBlock; ITextPointer pointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (context == TextPointerContext.None && !block.IsTail) { // get next block block = block.NextBlock; // get start pointer = block.ChildContainer.Start; context = pointer.GetPointerContext(direction); } } else { Debug.Assert(direction == LogicalDirection.Backward); while (context == TextPointerContext.None && !block.IsHead) { // get next block block = block.PreviousBlock; // get start pointer = block.ChildContainer.End; context = pointer.GetPointerContext(direction); } } return new DocumentSequenceTextPointer(block, pointer); } //----------------------------------------------------------------------- // Trusted Methods -- all positions are in valid blocks // // Each ChildDocumentBlock pair is separated by a gap (DocumentBreak), // which is surfaced as an embedded object. Besides the Head and Tail block, // each block is enclosed by two gap objects, one at each end of the // TextContainer (Before TextContainer.Start and After TextContainer.End) //----------------------------------------------------------------------- private static TextPointerContext xGapAwareGetSymbolType(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetPointerContext(direction); } private static object xGapAwareGetEmbeddedElement(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetAdjacentElement(direction); } // Intelligent compare routine that understands block gap // Since there it is assumed that there is an invisible Gap // object between adjancent two blocks, there is no position // overlap. private static int xGapAwareCompareTo(DocumentSequenceTextPointer thisTp, DocumentSequenceTextPointer tp) { Debug.Assert(tp != null); if ((object)thisTp == (object)tp) { return 0; } ChildDocumentBlock thisBlock = thisTp.ChildBlock; ChildDocumentBlock tpBlock = tp.ChildBlock; int comp = thisTp.AggregatedContainer.GetChildBlockDistance(thisBlock, tpBlock); if (comp == 0) { Debug.Assert(thisTp.ChildBlock.ChildContainer == tp.ChildBlock.ChildContainer); return thisTp.ChildPointer.CompareTo(tp.ChildPointer); } else if (comp < 0) { // thisBlock is after tpBlock return xUnseparated(tp, thisTp) ? 0 : 1; } else { // thisBlock is before tpBlock return xUnseparated(thisTp, tp) ? 0 : -1; } } private static bool xUnseparated(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { // tp1 is before tp2, check both are at edge of documents //check nothing of any length between them if (tp1.ChildPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None || tp2.ChildPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.None) { return false; } ChildDocumentBlock block = tp1.ChildBlock.NextBlock; while (block != tp2.ChildBlock) { if (block.ChildContainer.Start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { return false; } block = block.NextBlock; } return true; } // Get the count of symbols between two TP. // Gap aware // TP1 <= TP2 private static int xGapAwareGetDistance(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { Debug.Assert(xGapAwareCompareTo(tp1, tp2) <= 0); if (tp1 == tp2) { return 0; } int count = 0; DocumentSequenceTextPointer tpScan = new DocumentSequenceTextPointer(tp1.ChildBlock, tp1.ChildPointer); while (tpScan.ChildBlock != tp2.ChildBlock) { // Skip the entire block to the end count += tpScan.ChildPointer.GetOffsetToPosition(tpScan.ChildPointer.TextContainer.End); // Move on to next block ChildDocumentBlock nextBlock = tpScan.ChildBlock.NextBlock; tpScan.ChildBlock = nextBlock; tpScan.ChildPointer = nextBlock.ChildContainer.Start; } count += tpScan.ChildPointer.GetOffsetToPosition(tp2.ChildPointer); return count; } // Move this TP by distance, and respect virtualization of child TextContainer // Return true if distance is within boundary of the aggregated container, false otherwise private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { // // Note: To calculate distance between thisTp.ChildPointer to // it container Start/End position would have been devastating // for those who implemented vitualization. // Ideally we would need a new API on ITextPointer // ITextPointer.IsDistanceOutOfRange ChildDocumentBlock cdb = thisTp.ChildBlock; bool isNavigator = true; ITextPointer childTn = thisTp.ChildPointer; if (childTn == null) { isNavigator = false; childTn = thisTp.ChildPointer.CreatePointer(); } LogicalDirection scanDir = (distance > 0 ? LogicalDirection.Forward : LogicalDirection.Backward); distance = Math.Abs(distance); while (distance > 0) { TextPointerContext tst = childTn.GetPointerContext(scanDir); switch (tst) { case TextPointerContext.ElementStart: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.ElementEnd: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.EmbeddedElement: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.Text: int runLength = childTn.GetTextRunLength(scanDir); int moveLength = runLength < distance ? runLength : distance; distance -= moveLength; //agurcan: Fix for 1098225 //We need to propagate direction info to MoveByOffset if (scanDir == LogicalDirection.Backward) { moveLength *= -1; } childTn.MoveByOffset(moveLength); break; case TextPointerContext.None: if (!((cdb.IsHead && scanDir == LogicalDirection.Backward) || (cdb.IsTail && scanDir == LogicalDirection.Forward) ) ) { cdb = (scanDir == LogicalDirection.Forward ? cdb.NextBlock : cdb.PreviousBlock); childTn = (scanDir == LogicalDirection.Forward ? cdb.ChildContainer.Start.CreatePointer(childTn.LogicalDirection) : cdb.ChildContainer.End.CreatePointer(childTn.LogicalDirection) ); } else { return false; } break; default: Debug.Assert(false, "invalid TextPointerContext"); break; } } // Re-position thisTp to the new location. thisTp.ChildBlock = cdb; if (isNavigator) { thisTp.ChildPointer = childTn; } else { thisTp.ChildPointer = childTn.CreatePointer(); } return true; } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private ChildDocumentBlock _childBlock; private ITextPointer _childTp; // True if Freeze has been called, in which case // this TextPointer is immutable and may not be repositioned. private bool _isFrozen; #if DEBUG private uint _debugId = GetDebugId(); private static uint _debugIdCounter = 0; #endif #endregion Private Fields } } // 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: // DocumentSequenceTextPointer is an implementation of ITextPointer/ITextPointer // for FixedDocumentSequence. It is the base class for DocumentSequenceTextPointer and // DocumentSequenceTextPointer. // // History: // 07/12/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.Documents; using MS.Utility; using MS.Internal; using System.Windows; using System; using System.Diagnostics; ////// DocumentSequenceTextPointer is an implementation of ITextPointer for FixedDocumentSequence /// internal sealed class DocumentSequenceTextPointer : ContentPosition, ITextPointer { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // Ctor always set mutable flag to false internal DocumentSequenceTextPointer(ChildDocumentBlock childBlock, ITextPointer childPosition) { Debug.Assert(childBlock != null); Debug.Assert(childPosition != null); _childBlock = childBlock; _childTp = childPosition; } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region TextPointer Methods ////// void ITextPointer.SetLogicalDirection(LogicalDirection direction) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); _childTp.SetLogicalDirection(direction); } ////// /// int ITextPointer.CompareTo(ITextPointer position) { return DocumentSequenceTextPointer.CompareTo(this, position); } int ITextPointer.CompareTo(StaticTextPointer position) { return ((ITextPointer)this).CompareTo((ITextPointer)position.Handle0); } ////// /// int ITextPointer.GetOffsetToPosition(ITextPointer position) { return DocumentSequenceTextPointer.GetOffsetToPosition(this, position); } ////// /// TextPointerContext ITextPointer.GetPointerContext(LogicalDirection direction) { return DocumentSequenceTextPointer.GetPointerContext(this, direction); } ////// /// ////// Return 0 if non-text run int ITextPointer.GetTextRunLength(LogicalDirection direction) { return DocumentSequenceTextPointer.GetTextRunLength(this, direction); } //string ITextPointer.GetTextInRun(LogicalDirection direction) { return TextPointerBase.GetTextInRun(this, direction); } /// /// ////// Only reutrn uninterrupted runs of text int ITextPointer.GetTextInRun(LogicalDirection direction, char[] textBuffer, int startIndex, int count) { return DocumentSequenceTextPointer.GetTextInRun(this, direction, textBuffer, startIndex, count); } ////// ////// Return null if the embedded object does not exist object ITextPointer.GetAdjacentElement(LogicalDirection direction) { return DocumentSequenceTextPointer.GetAdjacentElement(this, direction); } ////// ////// Return null if no TextElement in the direction Type ITextPointer.GetElementType(LogicalDirection direction) { return DocumentSequenceTextPointer.GetElementType(this, direction); } ////// bool ITextPointer.HasEqualScope(ITextPointer position) { return DocumentSequenceTextPointer.HasEqualScope(this, position); } ////// /// ////// return property values even if there is no scoping element object ITextPointer.GetValue(DependencyProperty property) { return DocumentSequenceTextPointer.GetValue(this, property); } ////// ////// Throws InvalidOperationException if there is no scoping element object ITextPointer.ReadLocalValue(DependencyProperty property) { return DocumentSequenceTextPointer.ReadLocalValue(this, property); } ////// ////// Returns an empty enumerator if there is no scoping element LocalValueEnumerator ITextPointer.GetLocalValueEnumerator() { return DocumentSequenceTextPointer.GetLocalValueEnumerator(this); } ITextPointer ITextPointer.CreatePointer() { return DocumentSequenceTextPointer.CreatePointer(this); } // Unoptimized CreateStaticPointer implementation. // Creates a simple wrapper for an ITextPointer instance. StaticTextPointer ITextPointer.CreateStaticPointer() { return new StaticTextPointer(((ITextPointer)this).TextContainer, ((ITextPointer)this).CreatePointer()); } ITextPointer ITextPointer.CreatePointer(int distance) { return DocumentSequenceTextPointer.CreatePointer(this, distance); } ITextPointer ITextPointer.CreatePointer(LogicalDirection gravity) { return DocumentSequenceTextPointer.CreatePointer(this, gravity); } ////// ITextPointer ITextPointer.CreatePointer(int distance, LogicalDirection gravity) { return DocumentSequenceTextPointer.CreatePointer(this, distance, gravity); } ///// void ITextPointer.Freeze() { _isFrozen = true; } /// /// ITextPointer ITextPointer.GetFrozenPointer(LogicalDirection logicalDirection) { return TextPointerBase.GetFrozenPointer(this, logicalDirection); } ////// /// Inserts text at a specified position. /// /// /// Text to insert. /// void ITextPointer.InsertTextInRun(string textData) { throw new InvalidOperationException(SR.Get(SRID.DocumentReadOnly)); } ////// Removes content covered by a pair of positions. /// /// /// Position following the last symbol to delete. endPosition must be /// scoped by the same text element as startPosition. /// void ITextPointer.DeleteContentToPosition(ITextPointer limit) { throw new InvalidOperationException(SR.Get(SRID.DocumentReadOnly)); } // Candidate for replacing MoveToNextContextPosition for immutable TextPointer model ITextPointer ITextPointer.GetNextContextPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); if (pointer.MoveToNextContextPosition(direction)) { pointer.Freeze(); } else { pointer = null; } return pointer; } // Candidate for replacing MoveToInsertionPosition for immutable TextPointer model ITextPointer ITextPointer.GetInsertionPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); pointer.MoveToInsertionPosition(direction); pointer.Freeze(); return pointer; } // Returns the closest insertion position, treating all unicode code points // as valid insertion positions. A useful performance win over // GetNextInsertionPosition when only formatting scopes are important. ITextPointer ITextPointer.GetFormatNormalizedPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); TextPointerBase.MoveToFormatNormalizedPosition(pointer, direction); pointer.Freeze(); return pointer; } // Candidate for replacing MoveToNextInsertionPosition for immutable TextPointer model ITextPointer ITextPointer.GetNextInsertionPosition(LogicalDirection direction) { ITextPointer pointer = ((ITextPointer)this).CreatePointer(); if (pointer.MoveToNextInsertionPosition(direction)) { pointer.Freeze(); } else { pointer = null; } return pointer; } ///bool ITextPointer.ValidateLayout() { return TextPointerBase.ValidateLayout(this, ((ITextPointer)this).TextContainer.TextView); } #endregion TextPointer Methods #region Public Methods #if DEBUG /// /// Debug only ToString override. /// public override string ToString() { return DocumentSequenceTextPointer.ToString(this); } #endif // DEBUG #endregion Public Methods //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ #region TextPointer Properties //Type ITextPointer.ParentType { get { return DocumentSequenceTextPointer.GetElementType(this); } } /// /// ITextContainer ITextPointer.TextContainer { get { return this.AggregatedContainer; } } ///// bool ITextPointer.HasValidLayout { get { return (((ITextPointer)this).TextContainer.TextView != null && ((ITextPointer)this).TextContainer.TextView.IsValid && ((ITextPointer)this).TextContainer.TextView.Contains(this)); } } // bool ITextPointer.IsAtCaretUnitBoundary { get { Invariant.Assert(((ITextPointer)this).HasValidLayout); ITextView textView = ((ITextPointer)this).TextContainer.TextView; bool isAtCaretUnitBoundary = textView.IsAtCaretUnitBoundary(this); if (!isAtCaretUnitBoundary && ((ITextPointer)this).LogicalDirection == LogicalDirection.Backward) { // In MIL Text and TextView worlds, a position at trailing edge of a newline (with backward gravity) // is not an allowed caret stop. // However, in TextPointer world we must allow such a position to be a valid insertion position, // since it breaks textrange normalization for empty ranges. // Hence, we need to check for TextView.IsAtCaretUnitBoundary in reverse direction below. ITextPointer positionForwardGravity = ((ITextPointer)this).CreatePointer(LogicalDirection.Forward); isAtCaretUnitBoundary = textView.IsAtCaretUnitBoundary(positionForwardGravity); } return isAtCaretUnitBoundary; } } /// /// LogicalDirection ITextPointer.LogicalDirection { get { return _childTp.LogicalDirection; } } bool ITextPointer.IsAtInsertionPosition { get { return TextPointerBase.IsAtInsertionPosition(this); } } ///// bool ITextPointer.IsFrozen { get { return _isFrozen; } } // int ITextPointer.Offset { get { return TextPointerBase.GetOffset(this); } } // Not implemented. int ITextPointer.CharOffset { get { #pragma warning suppress 56503 throw new NotImplementedException(); } } #endregion TextPointer Properties //----------------------------------------------------- // // Public Events // //------------------------------------------------------ //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region TextNavigator Methods /// /// bool ITextPointer.MoveToNextContextPosition(LogicalDirection direction) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); return DocumentSequenceTextPointer.iScan(this, direction); } ////// /// int ITextPointer.MoveByOffset(int offset) { if (_isFrozen) throw new InvalidOperationException(SR.Get(SRID.TextPositionIsFrozen)); if (DocumentSequenceTextPointer.iScan(this, offset)) { return offset; } else { return 0; } } ////// /// void ITextPointer.MoveToPosition(ITextPointer position) { DocumentSequenceTextPointer tp = this.AggregatedContainer.VerifyPosition(position); Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); LogicalDirection gravity = this.ChildPointer.LogicalDirection; this.ChildBlock = tp.ChildBlock; if (this.ChildPointer.TextContainer == tp.ChildPointer.TextContainer) { this.ChildPointer.MoveToPosition(tp.ChildPointer); } else { this.ChildPointer = tp.ChildPointer.CreatePointer(); this.ChildPointer.SetLogicalDirection(gravity); } } ////// /// void ITextPointer.MoveToElementEdge(ElementEdge edge) { Debug.Assert(!_isFrozen, "Can't reposition a frozen pointer!"); this.ChildPointer.MoveToElementEdge(edge); } ///// int ITextPointer.MoveToLineBoundary(int count) { return TextPointerBase.MoveToLineBoundary(this, ((ITextPointer)this).TextContainer.TextView, count, true); } // Rect ITextPointer.GetCharacterRect(LogicalDirection direction) { return TextPointerBase.GetCharacterRect(this, direction); } bool ITextPointer.MoveToInsertionPosition(LogicalDirection direction) { return TextPointerBase.MoveToInsertionPosition(this, direction); } bool ITextPointer.MoveToNextInsertionPosition(LogicalDirection direction) { return TextPointerBase.MoveToNextInsertionPosition(this, direction); } #endregion TextNavigator Methods //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ #region Internal Properties internal DocumentSequenceTextContainer AggregatedContainer { get { return _childBlock.AggregatedContainer; } } // Accessor to base class ChildBlock. internal ChildDocumentBlock ChildBlock { get { return _childBlock; } set { _childBlock = value; } } // Helper exposing base classes ChildPointer as an ITextPointer, // which is guaranteed safe, since we set its value in the ctor // for this class. internal ITextPointer ChildPointer { get { return _childTp; } set { _childTp = value; Debug.Assert(_childTp != null); } } #if DEBUG // Debug-only identifier. private uint DebugId { get { return _debugId; } } #endif // DEBUG #endregion Internal Properties // ====================================================== // Static part of a class // // DocumentSequenceTextPointer is a static class that is provided all the // common functions of ITextPointer/ITextNavigaor for DocumentSequence. // Since we don't have multiple inheritance, this is a way to share code between // DocumentSequenceTextPointer and DocumentSequenceTextPointer. // //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region TextPointer Methods ////// public static int CompareTo(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); // Now do compare return xGapAwareCompareTo(thisTp, tp); } ////// /// public static int GetOffsetToPosition(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); int comp = xGapAwareCompareTo(thisTp, tp); if (comp == 0) { return 0; } else if (comp <= 0) { return xGapAwareGetDistance(thisTp, tp); } else { return -1 * xGapAwareGetDistance(tp, thisTp); } } ////// /// public static TextPointerContext GetPointerContext(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return xGapAwareGetSymbolType(thisTp, direction); } ////// /// ////// Return 0 if non-text run public static int GetTextRunLength(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return thisTp.ChildPointer.GetTextRunLength(direction); } ////// ////// Only reutrn uninterrupted runs of text public static int GetTextInRun(DocumentSequenceTextPointer thisTp, LogicalDirection direction, char[] textBuffer, int startIndex, int count) { ValidationHelper.VerifyDirection(direction, "direction"); if (textBuffer == null) { throw new ArgumentNullException("textBuffer"); } if (startIndex < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "startIndex")); } if (startIndex > textBuffer.Length) { throw new ArgumentException(SR.Get(SRID.StartIndexExceedsBufferSize, startIndex, textBuffer.Length)); } if (count < 0) { throw new ArgumentException(SR.Get(SRID.NegativeValue, "count")); } if (count > textBuffer.Length - startIndex) { throw new ArgumentException(SR.Get(SRID.MaxLengthExceedsBufferSize, count, textBuffer.Length, startIndex)); } return thisTp.ChildPointer.GetTextInRun(direction, textBuffer, startIndex, count); } ////// ////// Return null if the embedded object does not exist public static object GetAdjacentElement(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); return xGapAwareGetEmbeddedElement(thisTp, direction); } ////// ////// Return null if no TextElement in the direction public static Type GetElementType(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { ValidationHelper.VerifyDirection(direction, "direction"); DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetElementType(direction); } ////// public static Type GetElementType(DocumentSequenceTextPointer thisTp) { return thisTp.ChildPointer.ParentType; } ////// /// public static bool HasEqualScope(DocumentSequenceTextPointer thisTp, ITextPointer position) { DocumentSequenceTextPointer tp = thisTp.AggregatedContainer.VerifyPosition(position); if (thisTp.ChildPointer.TextContainer == tp.ChildPointer.TextContainer) { return thisTp.ChildPointer.HasEqualScope(tp.ChildPointer); } // The TextOM speced behavior is if both scopes are null, return true. return thisTp.ChildPointer.ParentType == typeof(FixedDocument) && tp.ChildPointer.ParentType == typeof(FixedDocument); } ////// /// ////// return property values even if there is no scoping element public static object GetValue(DocumentSequenceTextPointer thisTp, DependencyProperty property) { if (property == null) { throw new ArgumentNullException("property"); } return thisTp.ChildPointer.GetValue(property); } ////// ////// Throws InvalidOperationException if there is no scoping element public static object ReadLocalValue(DocumentSequenceTextPointer thisTp, DependencyProperty property) { if (property == null) { throw new ArgumentNullException("property"); } return thisTp.ChildPointer.ReadLocalValue(property); } ////// ////// Returns an empty enumerator if there is no scoping element public static LocalValueEnumerator GetLocalValueEnumerator(DocumentSequenceTextPointer thisTp) { return thisTp.ChildPointer.GetLocalValueEnumerator(); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp) { return CreatePointer(thisTp, 0, thisTp.ChildPointer.LogicalDirection); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, int distance) { return CreatePointer(thisTp, distance, thisTp.ChildPointer.LogicalDirection); } public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, LogicalDirection gravity) { return CreatePointer(thisTp, 0, gravity); } ////// public static ITextPointer CreatePointer(DocumentSequenceTextPointer thisTp, int distance, LogicalDirection gravity) { ValidationHelper.VerifyDirection(gravity, "gravity"); // Special case for common case of distance == 0 // to avoid calculating child container size, which // could be expansive especially in case where child // container requires virtualization. DocumentSequenceTextPointer newTp = new DocumentSequenceTextPointer(thisTp.ChildBlock, thisTp.ChildPointer.CreatePointer(gravity)); if (distance != 0) { if (!xGapAwareScan(newTp, distance)) { throw new ArgumentException(SR.Get(SRID.BadDistance), "distance"); } } return newTp; } #endregion TextPointer Methods //----------------------------------------------------- // // Public Properties // //----------------------------------------------------- //----------------------------------------------------- // // Public Events // //------------------------------------------------------ //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods // Internal Method, input parameter contains TP that is not synced to generation internal static bool iScan(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { bool moved = thisTp.ChildPointer.MoveToNextContextPosition(direction); if (!moved) { moved = xGapAwareScan(thisTp, (direction == LogicalDirection.Forward ? 1 : -1)); } return moved; } // Internal Method, input parameter contains TP that is not synced to generation internal static bool iScan(DocumentSequenceTextPointer thisTp, int distance) { return xGapAwareScan(thisTp, distance); } #if DEBUG // Allocate a unique debug-only ID as identifier. internal static uint GetDebugId() { return _debugIdCounter++; } internal static string ToString(DocumentSequenceTextPointer thisTp) { return (thisTp is DocumentSequenceTextPointer ? "DSTP" : "DSTN") + " Id=" + thisTp.DebugId + " B=" + thisTp.ChildBlock.DebugId + " G=" + thisTp.ChildPointer.LogicalDirection ; } #endif #endregion Internal Methods //------------------------------------------------------ // // Internal Property // //----------------------------------------------------- //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #region Private Methods private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext context = thisTp.ChildPointer.GetPointerContext(direction); if (context != TextPointerContext.None) { return thisTp; } ChildDocumentBlock block = thisTp.ChildBlock; ITextPointer pointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (context == TextPointerContext.None && !block.IsTail) { // get next block block = block.NextBlock; // get start pointer = block.ChildContainer.Start; context = pointer.GetPointerContext(direction); } } else { Debug.Assert(direction == LogicalDirection.Backward); while (context == TextPointerContext.None && !block.IsHead) { // get next block block = block.PreviousBlock; // get start pointer = block.ChildContainer.End; context = pointer.GetPointerContext(direction); } } return new DocumentSequenceTextPointer(block, pointer); } //----------------------------------------------------------------------- // Trusted Methods -- all positions are in valid blocks // // Each ChildDocumentBlock pair is separated by a gap (DocumentBreak), // which is surfaced as an embedded object. Besides the Head and Tail block, // each block is enclosed by two gap objects, one at each end of the // TextContainer (Before TextContainer.Start and After TextContainer.End) //----------------------------------------------------------------------- private static TextPointerContext xGapAwareGetSymbolType(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetPointerContext(direction); } private static object xGapAwareGetEmbeddedElement(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { DocumentSequenceTextPointer tp = xGetClingDSTP(thisTp, direction); return tp.ChildPointer.GetAdjacentElement(direction); } // Intelligent compare routine that understands block gap // Since there it is assumed that there is an invisible Gap // object between adjancent two blocks, there is no position // overlap. private static int xGapAwareCompareTo(DocumentSequenceTextPointer thisTp, DocumentSequenceTextPointer tp) { Debug.Assert(tp != null); if ((object)thisTp == (object)tp) { return 0; } ChildDocumentBlock thisBlock = thisTp.ChildBlock; ChildDocumentBlock tpBlock = tp.ChildBlock; int comp = thisTp.AggregatedContainer.GetChildBlockDistance(thisBlock, tpBlock); if (comp == 0) { Debug.Assert(thisTp.ChildBlock.ChildContainer == tp.ChildBlock.ChildContainer); return thisTp.ChildPointer.CompareTo(tp.ChildPointer); } else if (comp < 0) { // thisBlock is after tpBlock return xUnseparated(tp, thisTp) ? 0 : 1; } else { // thisBlock is before tpBlock return xUnseparated(thisTp, tp) ? 0 : -1; } } private static bool xUnseparated(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { // tp1 is before tp2, check both are at edge of documents //check nothing of any length between them if (tp1.ChildPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None || tp2.ChildPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.None) { return false; } ChildDocumentBlock block = tp1.ChildBlock.NextBlock; while (block != tp2.ChildBlock) { if (block.ChildContainer.Start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { return false; } block = block.NextBlock; } return true; } // Get the count of symbols between two TP. // Gap aware // TP1 <= TP2 private static int xGapAwareGetDistance(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { Debug.Assert(xGapAwareCompareTo(tp1, tp2) <= 0); if (tp1 == tp2) { return 0; } int count = 0; DocumentSequenceTextPointer tpScan = new DocumentSequenceTextPointer(tp1.ChildBlock, tp1.ChildPointer); while (tpScan.ChildBlock != tp2.ChildBlock) { // Skip the entire block to the end count += tpScan.ChildPointer.GetOffsetToPosition(tpScan.ChildPointer.TextContainer.End); // Move on to next block ChildDocumentBlock nextBlock = tpScan.ChildBlock.NextBlock; tpScan.ChildBlock = nextBlock; tpScan.ChildPointer = nextBlock.ChildContainer.Start; } count += tpScan.ChildPointer.GetOffsetToPosition(tp2.ChildPointer); return count; } // Move this TP by distance, and respect virtualization of child TextContainer // Return true if distance is within boundary of the aggregated container, false otherwise private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { // // Note: To calculate distance between thisTp.ChildPointer to // it container Start/End position would have been devastating // for those who implemented vitualization. // Ideally we would need a new API on ITextPointer // ITextPointer.IsDistanceOutOfRange ChildDocumentBlock cdb = thisTp.ChildBlock; bool isNavigator = true; ITextPointer childTn = thisTp.ChildPointer; if (childTn == null) { isNavigator = false; childTn = thisTp.ChildPointer.CreatePointer(); } LogicalDirection scanDir = (distance > 0 ? LogicalDirection.Forward : LogicalDirection.Backward); distance = Math.Abs(distance); while (distance > 0) { TextPointerContext tst = childTn.GetPointerContext(scanDir); switch (tst) { case TextPointerContext.ElementStart: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.ElementEnd: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.EmbeddedElement: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.Text: int runLength = childTn.GetTextRunLength(scanDir); int moveLength = runLength < distance ? runLength : distance; distance -= moveLength; //agurcan: Fix for 1098225 //We need to propagate direction info to MoveByOffset if (scanDir == LogicalDirection.Backward) { moveLength *= -1; } childTn.MoveByOffset(moveLength); break; case TextPointerContext.None: if (!((cdb.IsHead && scanDir == LogicalDirection.Backward) || (cdb.IsTail && scanDir == LogicalDirection.Forward) ) ) { cdb = (scanDir == LogicalDirection.Forward ? cdb.NextBlock : cdb.PreviousBlock); childTn = (scanDir == LogicalDirection.Forward ? cdb.ChildContainer.Start.CreatePointer(childTn.LogicalDirection) : cdb.ChildContainer.End.CreatePointer(childTn.LogicalDirection) ); } else { return false; } break; default: Debug.Assert(false, "invalid TextPointerContext"); break; } } // Re-position thisTp to the new location. thisTp.ChildBlock = cdb; if (isNavigator) { thisTp.ChildPointer = childTn; } else { thisTp.ChildPointer = childTn.CreatePointer(); } return true; } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private ChildDocumentBlock _childBlock; private ITextPointer _childTp; // True if Freeze has been called, in which case // this TextPointer is immutable and may not be repositioned. private bool _isFrozen; #if DEBUG private uint _debugId = GetDebugId(); private static uint _debugIdCounter = 0; #endif #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.///
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TimelineCollection.cs
- DataContractSerializerFaultFormatter.cs
- DesignerDataParameter.cs
- BaseProcessor.cs
- Resources.Designer.cs
- DependencyObjectPropertyDescriptor.cs
- InteropExecutor.cs
- ExpandableObjectConverter.cs
- Compilation.cs
- HttpChannelBindingToken.cs
- XmlResolver.cs
- DefaultProxySection.cs
- DateTimeSerializationSection.cs
- ConditionCollection.cs
- TextBoxAutoCompleteSourceConverter.cs
- BatchServiceHost.cs
- XmlWellformedWriterHelpers.cs
- HandlerBase.cs
- XmlSchemaAttributeGroup.cs
- KnownBoxes.cs
- NonSerializedAttribute.cs
- StatusBarPanelClickEvent.cs
- RayMeshGeometry3DHitTestResult.cs
- ComPlusServiceLoader.cs
- ContentTypeSettingDispatchMessageFormatter.cs
- Int64AnimationBase.cs
- __ComObject.cs
- SqlConnectionHelper.cs
- EventTrigger.cs
- GetMemberBinder.cs
- CommonDialog.cs
- SoapException.cs
- TransportBindingElement.cs
- DataReceivedEventArgs.cs
- RegexRunnerFactory.cs
- UnsafeMethods.cs
- NullExtension.cs
- DCSafeHandle.cs
- ListCollectionView.cs
- SerializationFieldInfo.cs
- TextRangeSerialization.cs
- RawAppCommandInputReport.cs
- SByte.cs
- ColumnResult.cs
- PackagePart.cs
- NetNamedPipeBindingCollectionElement.cs
- WebServiceEnumData.cs
- OdbcParameter.cs
- Overlapped.cs
- EntitySetBaseCollection.cs
- EndpointConfigContainer.cs
- HttpListenerRequest.cs
- MergeFilterQuery.cs
- SchemaImporter.cs
- TypeSystemHelpers.cs
- LineServicesRun.cs
- cookiecollection.cs
- Rfc2898DeriveBytes.cs
- XmlTextReaderImpl.cs
- OuterGlowBitmapEffect.cs
- RSAOAEPKeyExchangeFormatter.cs
- DecoderReplacementFallback.cs
- SmiRecordBuffer.cs
- Visual3D.cs
- WaitHandle.cs
- ProvidersHelper.cs
- SQLInt32Storage.cs
- FormatterConverter.cs
- RemotingServices.cs
- RadioButton.cs
- OracleTimeSpan.cs
- ImageAttributes.cs
- RealizedColumnsBlock.cs
- ActiveXHost.cs
- XmlSchemaElement.cs
- _LocalDataStore.cs
- WindowsTokenRoleProvider.cs
- PolicyException.cs
- CalendarDateRange.cs
- WhitespaceRule.cs
- InvalidChannelBindingException.cs
- FileNotFoundException.cs
- _AutoWebProxyScriptEngine.cs
- BuildManager.cs
- CompensatableSequenceActivity.cs
- ManifestSignedXml.cs
- BuilderInfo.cs
- UnsafeNativeMethods.cs
- SafeFileMappingHandle.cs
- MapPathBasedVirtualPathProvider.cs
- SqlUtil.cs
- JsonSerializer.cs
- TypedReference.cs
- nulltextcontainer.cs
- WebPartTracker.cs
- TaskCanceledException.cs
- DLinqDataModelProvider.cs
- ServiceContractViewControl.Designer.cs
- InputLangChangeRequestEvent.cs
- EditorZoneDesigner.cs