Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / documents / TextParagraphView.cs / 1 / TextParagraphView.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: TextView implementation for TextBlock.
//
// History:
// 03/04/2003 : [....] - Created
// 06/18/2003 : [....] - Ported to wcp tree
// 06/25/2004 : [....] - Performance work
//
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MS.Internal.Text;
using MS.Internal.PtsHost;
namespace MS.Internal.Documents
{
///
/// TextView implementation for TextBlock.
///
internal class TextParagraphView : TextViewBase
{
//-------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
///
/// Constructor.
///
///
/// Root of layout structure visualizing content.
///
///
/// TextContainer providing content for this view.
///
internal TextParagraphView(System.Windows.Controls.TextBlock owner, ITextContainer textContainer)
{
_owner = owner;
_textContainer = textContainer;
}
#endregion Constructors
//--------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
///
///
///
internal override ITextPointer GetTextPositionFromPoint(Point point, bool snapToText)
{
ITextPointer position;
// Verify that layout information is valid. Cannot continue if not valid.
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
// Retrieve position from line array.
position = GetTextPositionFromPoint(Lines, point, snapToText);
Invariant.Assert(position == null || position.HasValidLayout);
return position;
}
///
///
///
///
/// Always returns identity for output transform.
///
internal override Rect GetRawRectangleFromTextPosition(ITextPointer position, out Transform transform)
{
// Set transform to identity
transform = Transform.Identity;
// Verify that layout information is valid. Cannot continue if not valid.
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
if (position == null)
{
throw new ArgumentNullException("position");
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
return _owner.GetRectangleFromTextPosition(position);
}
///
///
///
internal override Geometry GetTightBoundingGeometryFromTextPositions(ITextPointer startPosition, ITextPointer endPosition)
{
#if TEXTPANELLAYOUTDEBUG
TextPanelDebug.StartTimer("TextView.GetTightBoundingGeometryFromTextPositions", TextPanelDebug.Category.TextView);
#endif
// Verify that layout information is valid. Cannot continue if not valid.
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
if (startPosition == null)
{
throw new ArgumentNullException("startPosition");
}
if (endPosition == null)
{
throw new ArgumentNullException("endPosition");
}
ValidationHelper.VerifyPosition(_textContainer, startPosition, "startPosition");
ValidationHelper.VerifyDirection(startPosition.LogicalDirection, "startPosition.LogicalDirection");
ValidationHelper.VerifyPosition(_textContainer, endPosition, "endPosition");
Geometry geometry = _owner.GetTightBoundingGeometryFromTextPositions(startPosition, endPosition);
#if TEXTPANELLAYOUTDEBUG
TextPanelDebug.StopTimer("TextView.GetTightBoundingGeometryFromTextPositions", TextPanelDebug.Category.TextView);
#endif
return (geometry);
}
///
///
///
internal override ITextPointer GetPositionAtNextLine(ITextPointer position, double suggestedX, int count, out double newSuggestedX, out int linesMoved)
{
ITextPointer positionOut;
// Verify that layout information is valid. Cannot continue if not valid.
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
if (position == null)
{
throw new ArgumentNullException("position");
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
// TextBlock element does not support columns, hence suggestedX does not change
// with line movement.
// Initialy set linesMoved to 0;
newSuggestedX = suggestedX;
linesMoved = 0;
if (count == 0)
{
return position;
}
ReadOnlyCollection lines = Lines;
Debug.Assert(lines != null && lines.Count > 0);
// Get index of the line that contains position.
int lineIndex = GetLineFromPosition(lines, position);
if (!(lineIndex >= 0 && lineIndex < lines.Count))
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("position");
}
// Advance line index by count.
int oldLineIndex = lineIndex;
lineIndex = Math.Max(0, lineIndex + count);
lineIndex = Math.Min(lines.Count - 1, lineIndex);
linesMoved = lineIndex - oldLineIndex;
// Get position at suggested X.
// If line has not been moved, return the same position.
// If suggested X is not provided, use the first position in the line.
if (linesMoved == 0)
{
positionOut = position;
}
else if (!DoubleUtil.IsNaN(suggestedX))
{
positionOut = lines[lineIndex].GetTextPositionFromDistance(suggestedX);
}
else
{
positionOut = lines[lineIndex].StartPosition.CreatePointer(LogicalDirection.Forward);
}
Invariant.Assert(positionOut == null || positionOut.HasValidLayout);
return positionOut;
}
///
///
///
internal override bool IsAtCaretUnitBoundary(ITextPointer position)
{
// Verify valid layout, position and direction
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
// No special cases for this, the only special case is handled in TextBlock
int lineIndex = GetLineFromPosition(Lines, position);
int dcp = Lines[lineIndex].StartPositionCP;
return _owner.IsAtCaretUnitBoundary(position, dcp, lineIndex);
}
///
///
///
internal override ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction)
{
// Verify valid layout, position and direction
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
// Get line index for position, and offset
int lineIndex = GetLineFromPosition(Lines, position);
int dcp = Lines[lineIndex].StartPositionCP;
ITextPointer positionOut = _owner.GetNextCaretUnitPosition(position, direction, dcp, lineIndex);
Invariant.Assert(positionOut == null || positionOut.HasValidLayout);
return positionOut;
}
///
///
///
internal override ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position)
{
// Verify valid layout, position and direction
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
// Get line index for position, and offset
int lineIndex = GetLineFromPosition(Lines, position);
int dcp = Lines[lineIndex].StartPositionCP;
ITextPointer positionOut = _owner.GetBackspaceCaretUnitPosition(position, dcp, lineIndex);
Invariant.Assert(positionOut == null || positionOut.HasValidLayout);
return positionOut;
}
///
///
///
internal override TextSegment GetLineRange(ITextPointer position)
{
ReadOnlyCollection lines;
int lineIndex;
// Verify that layout information is valid. Cannot continue if not valid.
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
if (position == null)
{
throw new ArgumentNullException("position");
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
lines = Lines;
Debug.Assert(lines != null && lines.Count > 0);
// Get index of the line that contains position.
lineIndex = GetLineFromPosition(lines, position);
Debug.Assert(lineIndex >= 0 && lineIndex < lines.Count);
return new TextSegment(lines[lineIndex].StartPosition, lines[lineIndex].GetContentEndPosition(), true);
}
///
///
///
internal override bool Contains(ITextPointer position)
{
// Verify that layout information is valid. Cannot continue if not valid.
if (position == null)
{
throw new ArgumentNullException("position");
}
ValidationHelper.VerifyPosition(_textContainer, position, "position");
if (!IsValid)
{
throw new InvalidOperationException(SR.Get(SRID.TextViewInvalidLayout));
}
// TextParagraphView has a single view that covers all its contents,
// and there is no background layou mechanism for TextParagraphView,
// so all positions considered contained in it.
return true;
}
///
///
///
internal override bool Validate()
{
_owner.UpdateLayout();
return this.IsValid;
}
///
/// HitTest a line array.
///
/// Collection of lines.
/// Point in pixel coordinates to test.
///
/// If true, this method must always return a positioned text position
/// (the closest position as calculated by the control's heuristics).
/// If false, this method should return null position, if the test
/// point does not fall within any character bounding box.
///
///
/// A text position and its orientation matching or closest to the point.
///
internal static ITextPointer GetTextPositionFromPoint(ReadOnlyCollection lines, Point point, bool snapToText)
{
int lineIndex;
ITextPointer orientedPosition;
Debug.Assert(lines != null && lines.Count > 0, "Line array is empty.");
// Figure out which line is the closest to the input pixel position.
lineIndex = GetLineFromPoint(lines, point, snapToText);
Debug.Assert(lineIndex < lines.Count);
// If no line is hit, return null oriented text position.
// Otherwise hittest line content.
if (lineIndex < 0)
{
orientedPosition = null;
}
else
{
// Get position from distance.
orientedPosition = lines[lineIndex].GetTextPositionFromDistance(point.X);
}
return orientedPosition;
}
///
/// Returns the index of line that contains specified position.
///
/// Collections of lines.
/// Position with orientation.
///
/// Returns the index of line that contains specified position,
/// or -1 if position is not in line array.
///
internal static int GetLineFromPosition(ReadOnlyCollection lines, ITextPointer position)
{
int lineIndex = -1;
int indexStart = 0;
int indexEnd = lines.Count - 1;
// Needs to be calculated this way (and not from start of text tree)
// to ensure we're comparing the right dcps (cell boundaries reset cps from results)
int dcp = lines[0].StartPosition.GetOffsetToPosition(position) + lines[0].StartPositionCP;
// Check if the position is within line array range. If not, return closest line.
if (dcp < lines[0].StartPositionCP ||
dcp > lines[lines.Count - 1].EndPositionCP)
{
return dcp < lines[0].StartPositionCP ? 0 : lines.Count - 1;
}
// Search for line that contains specified position.
// Use binary search.
lineIndex = 0;
while (indexStart < indexEnd)
{
// Get index of the next line for the search process.
if (indexEnd - indexStart < 2)
{
lineIndex = (lineIndex == indexStart) ? indexEnd : indexStart;
}
else
{
lineIndex = indexStart + (indexEnd - indexStart) / 2;
}
// Check if the line is found and reduce searching range if necessary.
if (dcp < lines[lineIndex].StartPositionCP)
{
indexEnd = lineIndex;
}
else if (dcp > lines[lineIndex].EndPositionCP)
{
indexStart = lineIndex;
}
else
{
if (dcp == lines[lineIndex].EndPositionCP)
{
if (position.LogicalDirection == LogicalDirection.Forward && (lineIndex != lines.Count - 1))
{
++lineIndex;
}
}
else if (dcp == lines[lineIndex].StartPositionCP)
{
if (position.LogicalDirection == LogicalDirection.Backward && lineIndex != 0)
{
--lineIndex;
}
}
break;
}
}
#if DEBUG
Debug.Assert(dcp >= lines[lineIndex].StartPositionCP);
Debug.Assert(dcp < lines[lineIndex].EndPositionCP ||
( dcp == lines[lineIndex].EndPositionCP &&
( position.LogicalDirection == LogicalDirection.Backward ||
(position.LogicalDirection == LogicalDirection.Forward && (lineIndex == lines.Count - 1)))));
#endif
return lineIndex;
}
///
/// Raise TextView.Updated event.
///
internal void OnUpdated()
{
OnUpdated(EventArgs.Empty);
}
///
/// Invalidate TextView internal state.
///
internal void Invalidate()
{
_lines = null;
}
#endregion Internal Methods
//--------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------
#region Internal Properties
///
///
///
internal override UIElement RenderScope
{
get { return _owner; }
}
///
///
///
internal override ITextContainer TextContainer
{
get { return _textContainer; }
}
///
///
///
internal override bool IsValid
{
get { return _owner.IsLayoutDataValid; }
}
///
///
///
internal override ReadOnlyCollection TextSegments
{
get
{
List segments = new List(1);
segments.Add(new TextSegment(_textContainer.Start, _textContainer.End, true));
return new ReadOnlyCollection(segments);
}
}
///
/// Collection of LineResults for each line in the paragraph.
///
internal ReadOnlyCollection Lines
{
get
{
if (_lines == null)
{
_lines = _owner.GetLineResults();
}
return _lines;
}
}
#endregion Internal Properties
//-------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------
#region Private Methods
///
/// HitTest a line array.
///
/// Collection of lines.
/// Point in pixel coordinates to test.
///
/// If true, this method must always return a line index
/// (the closest position as calculated by the control's heuristics).
/// If false, this method should return -1, if the test
/// point does not fall within any character bounding box.
///
///
/// An index of line matching or closest to the point.
///
internal static int GetLineFromPoint(ReadOnlyCollection lines, Point point, bool snapToText)
{
Debug.Assert(lines != null && lines.Count > 0);
int lineIndex;
bool foundHit;
// Figure out which line is the closest vertically to the input pixel position.
// Assume fixed line height to find a starting point for search.
// If the first pick is not accurate, do linear search.
foundHit = GetVerticalLineFromPoint(lines, point, snapToText, out lineIndex);
// It is possible to have successive lines with the same
// vertical offset. It may happen when a line of text is split
// because of figure/floater.
// Figure out which line is the closest horizontally to the input pixel position.
if (foundHit)
{
foundHit = GetHorizontalLineFromPoint(lines, point, snapToText, ref lineIndex);
}
return foundHit ? lineIndex : -1;
}
///
/// HitTest a line array and find the index of line hit in vertical direction.
///
/// Collection of lines.
/// Point in pixel coordinates to test.
///
/// If true, this method must always return a line index
/// (the closest position as calculated by the control's heuristics).
/// If false, this method should return -1, if the test
/// point does not fall within any character bounding box.
///
/// Index of line that has been hit.
/// True if hit has been found.
private static bool GetVerticalLineFromPoint(ReadOnlyCollection lines, Point point, bool snapToText, out int lineIndex)
{
Debug.Assert(lines != null && lines.Count > 0);
bool foundHit = false;
double approximatedLineHeight;
// Figure out which line is the closest vertically to the input pixel position.
// Assume fixed line height to find a starting point for search.
// If the first pick is not accurate, do linear search.
approximatedLineHeight = lines[0].LayoutBox.Height;
lineIndex = Math.Max(Math.Min((int)(point.Y / approximatedLineHeight), lines.Count - 1), 0);
while (!foundHit)
{
Rect lineBox = lines[lineIndex].LayoutBox;
if (point.Y < lineBox.Y)
{
// Go to the previous line, if this is not the first one.
if (lineIndex > 0)
{
--lineIndex;
}
else
{
// This is the first line.
foundHit = snapToText;
break;
}
}
else if (point.Y > lineBox.Y + lineBox.Height)
{
// Go to the next line, if this is not the last one.
// But if the point belongs to the gap between lines,
// consider the closest line.
if (lineIndex < lines.Count - 1)
{
Rect nextLineBox = lines[lineIndex + 1].LayoutBox;
if (point.Y < nextLineBox.Y)
{
// Point is in the gap between lines. Use the closest line.
double gap = nextLineBox.Y - (lineBox.Y + lineBox.Height);
if (point.Y > lineBox.Y + lineBox.Height + gap / 2)
{
++lineIndex;
}
foundHit = snapToText;
break;
}
else
{
++lineIndex;
}
}
else
{
// This is the last line.
foundHit = snapToText;
break;
}
}
else
{
// The current line has been hit.
// But in the case of line overlapping, consider the closest line.
Rect siblingLineBox;
double siblingOverhang;
// Check the previous line overhang.
siblingOverhang = 0;
if (lineIndex > 0)
{
siblingLineBox = lines[lineIndex - 1].LayoutBox;
siblingOverhang = lineBox.Y - (siblingLineBox.Y + siblingLineBox.Height);
}
if (siblingOverhang < 0)
{
// The current line overlaps with the previous line.
// Use the closest one.
if (point.Y < lineBox.Y - siblingOverhang / 2)
{
--lineIndex;
}
}
else
{
// Check the next line overhang.
siblingOverhang = 0;
if (lineIndex < lines.Count - 1)
{
siblingLineBox = lines[lineIndex + 1].LayoutBox;
siblingOverhang = siblingLineBox.Y - (lineBox.Y + lineBox.Height);
}
if (siblingOverhang < 0)
{
// The current line overlaps with the next line.
// Use the closest one.
if (point.Y > lineBox.Y + lineBox.Height + siblingOverhang / 2)
{
++lineIndex;
}
}
}
foundHit = true;
break;
}
}
return foundHit;
}
///
/// HitTest a line array and find the index of line hit in horizontal direction.
/// Assumes that vertical hittesting has been already done and lineIndex points to
/// index of line that has been hit in vertical direction.
///
/// Collection of lines.
/// Point in pixel coordinates to test.
///
/// If true, this method must always return a line index
/// (the closest position as calculated by the control's heuristics).
/// If false, this method should return -1, if the test
/// point does not fall within any character bounding box.
///
/// Index of line that has been hit.
/// True if hit has been found.
private static bool GetHorizontalLineFromPoint(ReadOnlyCollection lines, Point point, bool snapToText, ref int lineIndex)
{
Debug.Assert(lines != null && lines.Count > 0);
bool foundHit = false;
bool lookForSiblings = true;
// It is possible to have successive lines with the same
// vertical offset. It may happen when a line of text is split
// because of figure/floater.
// Figure out which line is the closest horizontally to the input pixel position.
while (lookForSiblings)
{
Rect lineBox = lines[lineIndex].LayoutBox;
Rect siblingLineBox;
double siblingGap;
// Check sibling lines.
if (point.X < lineBox.X && lineIndex > 0)
{
// Check if the previous line starts at the same vertical position.
siblingLineBox = lines[lineIndex - 1].LayoutBox;
if (DoubleUtil.AreClose(siblingLineBox.Y, lineBox.Y))
{
if (point.X <= siblingLineBox.X + siblingLineBox.Width)
{
--lineIndex;
}
else
{
siblingGap = Math.Max(lineBox.X - (siblingLineBox.X + siblingLineBox.Width), 0);
if (point.X < lineBox.X - siblingGap / 2)
{
--lineIndex;
}
foundHit = snapToText;
lookForSiblings = false;
break;
}
}
else
{
foundHit = snapToText;
lookForSiblings = false;
break;
}
}
else if ((point.X > lineBox.X + lineBox.Width) && (lineIndex < lines.Count - 1))
{
// Check if the next line starts at the same vertical position.
siblingLineBox = lines[lineIndex + 1].LayoutBox;
if (DoubleUtil.AreClose(siblingLineBox.Y, lineBox.Y))
{
if (point.X >= siblingLineBox.X)
{
++lineIndex;
}
else
{
siblingGap = Math.Max(siblingLineBox.X - (lineBox.X + lineBox.Width), 0);
if (point.X > siblingLineBox.X - siblingGap / 2)
{
++lineIndex;
}
foundHit = snapToText;
lookForSiblings = false;
break;
}
}
else
{
foundHit = snapToText;
lookForSiblings = false;
break;
}
}
else
{
foundHit = snapToText || (point.X >= lineBox.X && point.X <= lineBox.X + lineBox.Width);
lookForSiblings = false;
break;
}
}
return foundHit;
}
#endregion Private methods
//-------------------------------------------------------------------
//
// Private Fields
//
//-------------------------------------------------------------------
#region Private Fields
///
/// Root of layout structure visualizing content.
///
//
private readonly System.Windows.Controls.TextBlock _owner;
///
/// TextContainer providing content for this view.
///
private readonly ITextContainer _textContainer;
///
/// Cached collection of LineResults.
///
private ReadOnlyCollection _lines;
#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
- XmlSchemaComplexContentExtension.cs
- ToolboxBitmapAttribute.cs
- Variant.cs
- DefaultMemberAttribute.cs
- SystemInfo.cs
- MappingModelBuildProvider.cs
- CreateUserErrorEventArgs.cs
- AppendHelper.cs
- DispatcherOperation.cs
- connectionpool.cs
- StartUpEventArgs.cs
- PageDeviceFont.cs
- WebHttpSecurityElement.cs
- XmlToDatasetMap.cs
- DrawingContextWalker.cs
- VirtualPath.cs
- ProcessHostConfigUtils.cs
- BufferedGraphicsContext.cs
- CollectionBuilder.cs
- SafeFileMappingHandle.cs
- JsonWriter.cs
- LZCodec.cs
- FieldToken.cs
- PresentationAppDomainManager.cs
- Parser.cs
- CodeTypeOfExpression.cs
- Int64AnimationUsingKeyFrames.cs
- FileLevelControlBuilderAttribute.cs
- Page.cs
- BrowserCapabilitiesCodeGenerator.cs
- StandardBindingElementCollection.cs
- OpenTypeLayoutCache.cs
- ActivityBuilderHelper.cs
- BindingValueChangedEventArgs.cs
- MenuItem.cs
- BatchServiceHost.cs
- WindowsGraphicsWrapper.cs
- CodeTypeConstructor.cs
- DataGrid.cs
- StatusBarItem.cs
- OleStrCAMarshaler.cs
- LineServices.cs
- ServiceBehaviorElement.cs
- FixedSOMLineRanges.cs
- WindowsAuthenticationModule.cs
- UnsafeCollabNativeMethods.cs
- SiteMapDataSourceDesigner.cs
- Accessors.cs
- WindowsGraphicsWrapper.cs
- OneOfElement.cs
- TreeNode.cs
- SvcMapFileLoader.cs
- TransformerTypeCollection.cs
- ISCIIEncoding.cs
- LabelLiteral.cs
- GridViewRowCollection.cs
- XamlWriter.cs
- TPLETWProvider.cs
- TypeLibConverter.cs
- RandomNumberGenerator.cs
- HwndSourceKeyboardInputSite.cs
- ExclusiveCanonicalizationTransform.cs
- OleAutBinder.cs
- ThreadStartException.cs
- WindowsImpersonationContext.cs
- PasswordDeriveBytes.cs
- ProxyFragment.cs
- MetadataItemCollectionFactory.cs
- FormViewInsertEventArgs.cs
- DecimalConverter.cs
- XsltSettings.cs
- SubstitutionList.cs
- ObjectNotFoundException.cs
- DtdParser.cs
- CodeObject.cs
- MetricEntry.cs
- CqlBlock.cs
- TypeCacheManager.cs
- TripleDES.cs
- ReadOnlyTernaryTree.cs
- WindowsAuthenticationModule.cs
- AddInPipelineAttributes.cs
- NativeMethods.cs
- SoapClientMessage.cs
- ServiceOperationViewControl.cs
- DigitalSignature.cs
- LogAppendAsyncResult.cs
- DesignerForm.cs
- TextBoxAutomationPeer.cs
- Function.cs
- RequestCacheManager.cs
- TextSpanModifier.cs
- AgileSafeNativeMemoryHandle.cs
- IpcPort.cs
- BasicHttpSecurityElement.cs
- Semaphore.cs
- DbConnectionPoolCounters.cs
- ResourceDefaultValueAttribute.cs
- ToolStripDesignerAvailabilityAttribute.cs
- ValidationErrorCollection.cs