Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / documents / TextContainerHelper.cs / 1 / TextContainerHelper.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: Helper services for TextContainer.
//
// History:
// 07/26/2004 : grzegorz - Created.
//
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MS.Internal.Text;
namespace MS.Internal.Documents
{
internal sealed class TextContentRange
{
internal TextContentRange()
{
}
internal TextContentRange(int cpFirst, int cpLast, ITextContainer textContainer)
{
Invariant.Assert(cpFirst <= cpLast);
Invariant.Assert(cpFirst >= 0);
Invariant.Assert(textContainer != null);
Invariant.Assert(cpLast <= textContainer.SymbolCount);
_cpFirst = cpFirst;
_cpLast = cpLast;
_size = 0;
_ranges = null;
_textContainer = textContainer;
}
internal void Merge(TextContentRange other)
{
Invariant.Assert(other != null);
// Skip merge operation if we're merging an empty text content range.
if(other._textContainer == null)
{
return;
}
if (_textContainer == null)
{
_cpFirst = other._cpFirst;
_cpLast = other._cpLast;
_textContainer = other._textContainer;
_size = other._size;
if (_size != 0)
{
Invariant.Assert(other._ranges != null);
Invariant.Assert(other._ranges.Length >= (other._size * 2));
_ranges = new int[_size * 2];
for (int i = 0; i < _ranges.Length; i++)
{
_ranges[i] = other._ranges[i];
}
}
}
else
{
Invariant.Assert(_textContainer == other._textContainer);
if (other.IsSimple)
{
Merge(other._cpFirst, other._cpLast);
}
else
{
for (int i = 0; i < other._size; i++)
{
Merge(other._ranges[i * 2], other._ranges[i * 2 + 1]);
}
}
}
Normalize();
}
internal ReadOnlyCollection GetTextSegments()
{
List segments;
if (_textContainer == null)
{
segments = new List();
}
else
{
if (IsSimple)
{
segments = new List(1);
segments.Add(new TextSegment(
_textContainer.CreatePointerAtOffset(_cpFirst, LogicalDirection.Forward),
_textContainer.CreatePointerAtOffset(_cpLast, LogicalDirection.Backward),
true));
}
else
{
segments = new List(_size);
for (int i = 0; i < _size; i++)
{
segments.Add(new TextSegment(
_textContainer.CreatePointerAtOffset(_ranges[i * 2], LogicalDirection.Forward),
_textContainer.CreatePointerAtOffset(_ranges[i * 2 + 1], LogicalDirection.Backward),
true));
}
}
}
return new ReadOnlyCollection(segments);
}
internal bool Contains(ITextPointer position, bool strict)
{
bool contains = false;
int cpPos = position.Offset;
if (IsSimple)
{
if (cpPos >= _cpFirst && cpPos <= _cpLast)
{
contains = true;
if (strict && (_cpFirst != _cpLast))
{
if (cpPos == _cpFirst && position.LogicalDirection == LogicalDirection.Backward ||
cpPos == _cpLast && position.LogicalDirection == LogicalDirection.Forward)
{
contains = false;
}
}
}
}
else
{
for (int i = 0; i < _size; i++)
{
if (cpPos >= _ranges[i * 2] && cpPos <= _ranges[i * 2 + 1])
{
contains = true;
if (strict)
{
if (cpPos == _ranges[i * 2] && position.LogicalDirection == LogicalDirection.Backward ||
cpPos == _ranges[i * 2 + 1] && position.LogicalDirection == LogicalDirection.Forward)
{
contains = false;
}
}
break;
}
}
}
return contains;
}
internal ITextPointer StartPosition
{
get
{
ITextPointer startPosition = null;
if (_textContainer != null)
{
startPosition = _textContainer.CreatePointerAtOffset(IsSimple ? _cpFirst : _ranges[0], LogicalDirection.Forward);
}
return startPosition;
}
}
internal ITextPointer EndPosition
{
get
{
ITextPointer endPosition = null;
if (_textContainer != null)
{
endPosition = _textContainer.CreatePointerAtOffset(IsSimple ? _cpLast : _ranges[(_size - 1) * 2 + 1], LogicalDirection.Backward);
}
return endPosition;
}
}
private void Merge(int cpFirst, int cpLast)
{
if (IsSimple)
{
if (cpFirst > _cpLast || cpLast < _cpFirst)
{
_size = 2;
_ranges = new int[8]; // 4 entries
if (cpFirst > _cpLast)
{
_ranges[0] = _cpFirst;
_ranges[1] = _cpLast;
_ranges[2] = cpFirst;
_ranges[3] = cpLast;
}
else
{
_ranges[0] = cpFirst;
_ranges[1] = cpLast;
_ranges[2] = _cpFirst;
_ranges[3] = _cpLast;
}
}
else
{
_cpFirst = Math.Min(_cpFirst, cpFirst);
_cpLast = Math.Max(_cpLast, cpLast);
}
}
else
{
int i = 0;
while (i < _size)
{
if (cpLast < _ranges[i * 2])
{
// Insert before the current position
EnsureSize();
for (int j = _size * 2 - 1; j >= i * 2; j--)
{
_ranges[j + 2] = _ranges[j];
}
_ranges[i * 2] = cpFirst;
_ranges[i * 2 + 1] = cpLast;
++_size;
break;
}
else if (cpFirst <= _ranges[i * 2 + 1])
{
// Merge with the current position
_ranges[i * 2] = Math.Min(_ranges[i * 2], cpFirst);
_ranges[i * 2 + 1] = Math.Max(_ranges[i * 2 + 1], cpLast);
while (MergeWithNext(i)) { }
break;
}
++i;
}
if (i >= _size)
{
// Insert at the last position
EnsureSize();
_ranges[_size * 2] = cpFirst;
_ranges[_size * 2 + 1] = cpLast;
++_size;
}
}
}
private bool MergeWithNext(int pos)
{
if (pos < _size - 1)
{
if (_ranges[pos * 2 + 1] >= _ranges[(pos + 1) * 2])
{
_ranges[pos * 2 + 1] = Math.Max(_ranges[pos * 2 + 1], _ranges[(pos + 1) * 2 + 1]);
for (int i = (pos + 1) * 2; i < (_size - 1) * 2; i++)
{
_ranges[i] = _ranges[i + 2];
}
--_size;
return true;
}
}
return false;
}
private void EnsureSize()
{
Invariant.Assert(_size > 0);
Invariant.Assert(_ranges != null);
if (_ranges.Length < (_size + 1) * 2)
{
int[] ranges = new int[_ranges.Length * 2];
for (int i = 0; i < _size * 2; i++)
{
ranges[i] = _ranges[i];
}
_ranges = ranges;
}
}
private void Normalize()
{
if (_size == 1)
{
_cpFirst = _ranges[0];
_cpLast = _ranges[1];
_size = 0;
_ranges = null;
}
}
private bool IsSimple { get { return (_size == 0); } }
private int _cpFirst;
private int _cpLast;
private int _size;
private int[] _ranges;
private ITextContainer _textContainer;
}
///
/// Helper services for TextContainer.
///
internal static class TextContainerHelper
{
//-------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
///
/// Retrieves a collection of AutomationPeers that fall within the range.
/// Children that overlap with the range but are not entirely enclosed by
/// it will also be included in the collection.
///
internal static List GetAutomationPeersFromRange(ITextPointer start, ITextPointer end, ITextPointer ownerContentStart)
{
bool positionMoved;
AutomationPeer peer = null;
object element;
List peers = new List();
start = start.CreatePointer();
while (start.CompareTo(end) < 0)
{
// Indicate that 'start' position is not moved yet.
positionMoved = false;
if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart)
{
// Get adjacent element and try to retrive AutomationPeer for it.
element = start.GetAdjacentElement(LogicalDirection.Forward);
if (element is ContentElement)
{
peer = ContentElementAutomationPeer.CreatePeerForElement((ContentElement)element);
// If AutomationPeer has been retrieved, add it to the collection.
// And skip entire element.
if (peer != null)
{
if (ownerContentStart == null || IsImmediateAutomationChild(start, ownerContentStart))
{
peers.Add(peer);
}
start.MoveToNextContextPosition(LogicalDirection.Forward);
start.MoveToElementEdge(ElementEdge.AfterEnd);
positionMoved = true;
}
}
}
else if (start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.EmbeddedElement)
{
// Get adjacent element and try to retrive AutomationPeer for it.
element = start.GetAdjacentElement(LogicalDirection.Forward);
if (element is UIElement)
{
if (ownerContentStart == null || IsImmediateAutomationChild(start, ownerContentStart))
{
peer = UIElementAutomationPeer.CreatePeerForElement((UIElement)element);
// If AutomationPeer has been retrieved, add it to the collection.
if (peer != null)
{
peers.Add(peer);
}
else
{
iterate((Visual)element, peers);
}
}
}
else if (element is ContentElement)
{
peer = ContentElementAutomationPeer.CreatePeerForElement((ContentElement)element);
// If AutomationPeer has been retrieved, add it to the collection.
if (peer != null)
{
if (ownerContentStart == null || IsImmediateAutomationChild(start, ownerContentStart))
{
peers.Add(peer);
}
}
}
}
// Move to the next content position, if position has not been moved already.
if (!positionMoved)
{
if (!start.MoveToNextContextPosition(LogicalDirection.Forward))
{
break;
}
}
}
return peers;
}
///
/// Is AutometionPeer represented by 'elementStart' is immediate child of AutomationPeer
/// represented by 'ownerContentStart'.
///
internal static bool IsImmediateAutomationChild(ITextPointer elementStart, ITextPointer ownerContentStart)
{
Invariant.Assert(elementStart.CompareTo(ownerContentStart) >= 0);
bool immediateChild = true;
// Walk element tree up looking for AutomationPeers.
ITextPointer position = elementStart.CreatePointer();
while (typeof(TextElement).IsAssignableFrom(position.ParentType))
{
position.MoveToElementEdge(ElementEdge.BeforeStart);
if (position.CompareTo(ownerContentStart) <= 0)
{
break;
}
AutomationPeer peer = null;
object element = position.GetAdjacentElement(LogicalDirection.Forward);
if (element is UIElement)
{
peer = UIElementAutomationPeer.CreatePeerForElement((UIElement)element);
}
else if (element is ContentElement)
{
peer = ContentElementAutomationPeer.CreatePeerForElement((ContentElement)element);
}
if (peer != null)
{
immediateChild = false;
break;
}
}
return immediateChild;
}
///
/// Returns the common ancestor of two positions. This ancestor needs to have
/// AutomationPeer associated with it.
///
internal static AutomationPeer GetEnclosingAutomationPeer(ITextPointer start, ITextPointer end, out ITextPointer elementStart, out ITextPointer elementEnd)
{
object element;
AutomationPeer peer;
ITextPointer position;
List