Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / 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
- GetPageCompletedEventArgs.cs
- _Events.cs
- CompilationLock.cs
- Walker.cs
- GlobalAllocSafeHandle.cs
- CheckoutException.cs
- CommandEventArgs.cs
- ColumnResult.cs
- SiteMapDataSourceView.cs
- EndpointDiscoveryMetadata11.cs
- DelayDesigner.cs
- unitconverter.cs
- GridViewHeaderRowPresenterAutomationPeer.cs
- XMLUtil.cs
- ScrollBarRenderer.cs
- PerCallInstanceContextProvider.cs
- ElasticEase.cs
- ScriptHandlerFactory.cs
- TextMetrics.cs
- oledbconnectionstring.cs
- XhtmlBasicSelectionListAdapter.cs
- ChangeConflicts.cs
- mactripleDES.cs
- NavigationService.cs
- FileDialog.cs
- RootBrowserWindow.cs
- XmlHierarchyData.cs
- IfElseDesigner.xaml.cs
- PrintPageEvent.cs
- XmlIncludeAttribute.cs
- UserNameSecurityTokenParameters.cs
- TypedReference.cs
- CheckPair.cs
- InfoCardSymmetricCrypto.cs
- panel.cs
- XmlSchemaDocumentation.cs
- Tile.cs
- GridViewRowCollection.cs
- UnsafeNativeMethods.cs
- AutoResetEvent.cs
- CustomErrorsSection.cs
- ToolStripRendererSwitcher.cs
- ErrorFormatter.cs
- ContextStack.cs
- DragEvent.cs
- CodeParameterDeclarationExpression.cs
- UnsafeNativeMethods.cs
- StorageEntityContainerMapping.cs
- PaintValueEventArgs.cs
- datacache.cs
- odbcmetadatafactory.cs
- IndentedWriter.cs
- DocumentReference.cs
- DataColumnMapping.cs
- XmlRawWriter.cs
- DocumentPageTextView.cs
- ImageButton.cs
- ProxyAssemblyNotLoadedException.cs
- GregorianCalendar.cs
- ManagedWndProcTracker.cs
- Blend.cs
- SqlRowUpdatingEvent.cs
- PrePostDescendentsWalker.cs
- SoapFaultCodes.cs
- RunClient.cs
- TypeReference.cs
- ProjectionPathSegment.cs
- EntityException.cs
- DataGridViewCellValueEventArgs.cs
- UnsafeMethods.cs
- FixedBufferAttribute.cs
- HitTestWithPointDrawingContextWalker.cs
- BuildResultCache.cs
- odbcmetadatafactory.cs
- ToolTip.cs
- CurrentTimeZone.cs
- Identity.cs
- HtmlAnchor.cs
- SmiMetaDataProperty.cs
- UpdateCommandGenerator.cs
- ParameterBuilder.cs
- XmlResolver.cs
- ProxyGenerationError.cs
- ScrollBar.cs
- AssociationSetMetadata.cs
- AlternateView.cs
- ObjectHandle.cs
- TableHeaderCell.cs
- LazyInitializer.cs
- DisplayMemberTemplateSelector.cs
- RegisteredDisposeScript.cs
- Queue.cs
- MeasureItemEvent.cs
- ListViewCancelEventArgs.cs
- SchemaCreator.cs
- CommentGlyph.cs
- SHA1Managed.cs
- mda.cs
- SecurityRuntime.cs
- LogicalExpr.cs