Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Text / ComplexLine.cs / 2 / ComplexLine.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// File: ComplexLine.cs
//
// Description: Text line formatter.
//
// History:
// 09/10/2003 : [....] - created.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using MS.Internal.Documents;
using MS.Internal.PtsHost;
namespace MS.Internal.Text
{
// ---------------------------------------------------------------------
// Text line formatter.
// ---------------------------------------------------------------------
internal sealed class ComplexLine : Line
{
// ------------------------------------------------------------------
//
// TextSource Implementation
//
// -----------------------------------------------------------------
#region TextSource Implementation
// ------------------------------------------------------------------
// Get a text run at specified text source position.
// ------------------------------------------------------------------
public override TextRun GetTextRun(int dcp)
{
TextRun run = null;
StaticTextPointer position = _owner.TextContainer.CreateStaticPointerAtOffset(dcp);
switch (position.GetPointerContext(LogicalDirection.Forward))
{
case TextPointerContext.Text:
run = HandleText(position);
break;
case TextPointerContext.ElementStart:
run = HandleElementStartEdge(position);
break;
case TextPointerContext.ElementEnd:
run = HandleElementEndEdge(position);
break;
case TextPointerContext.EmbeddedElement:
run = HandleInlineObject(position, dcp);
break;
case TextPointerContext.None:
run = new TextEndOfParagraph(_syntheticCharacterLength);
break;
}
Debug.Assert(run != null, "TextRun has not been created.");
Debug.Assert(run.Length > 0, "TextRun has to have positive length.");
return run;
}
// -----------------------------------------------------------------
// Get text immediately before specified text source position.
// ------------------------------------------------------------------
public override TextSpan GetPrecedingText(int dcp)
{
// Parameter validation
Debug.Assert(dcp >= 0);
int nonTextLength = 0;
CharacterBufferRange precedingText = CharacterBufferRange.Empty;
CultureInfo culture = null;
if (dcp > 0)
{
// Create TextPointer at dcp
ITextPointer position = _owner.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward);
// Move backward until we find a position at the end of a text run, or reach start of TextContainer
while (position.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text &&
position.CompareTo(_owner.TextContainer.Start) != 0)
{
position.MoveByOffset(-1);
nonTextLength++;
}
// Return text in run. If it is at start of TextContainer this will return an empty string
string precedingTextString = position.GetTextInRun(LogicalDirection.Backward);
precedingText = new CharacterBufferRange(precedingTextString, 0, precedingTextString.Length);
StaticTextPointer pointer = position.CreateStaticPointer();
DependencyObject element = (pointer.Parent != null) ? pointer.Parent : _owner;
culture = DynamicPropertyReader.GetCultureInfo(element);
}
return new TextSpan(
nonTextLength + precedingText.Length,
new CultureSpecificCharacterBufferRange(culture, precedingText)
);
}
///
/// TextFormatter to map a text source character index to a text effect character index
///
/// text source character index
/// the text effect index corresponding to the text effect character index
public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(
int textSourceCharacterIndex
)
{
return textSourceCharacterIndex;
}
#endregion TextSource Implementation
//-------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------
#region Internal Methods
// -----------------------------------------------------------------
// Constructor.
//
// owner - owner of the line.
// ------------------------------------------------------------------
internal ComplexLine(System.Windows.Controls.TextBlock owner) : base(owner)
{
}
// -----------------------------------------------------------------
// Arrange content of formatted line.
//
// vc - Visual collection of the parent.
// lineOffset - Offset of the line.
// ------------------------------------------------------------------
internal override void Arrange(VisualCollection vc, Vector lineOffset)
{
// Arrange inline objects
int runDcp = _dcp;
IList> runs = _line.GetTextRunSpans();
Debug.Assert(runs != null, "Cannot retrieve runs collection.");
// Calculate offset shift due to trailing spaces
double adjustedXOffset = lineOffset.X + CalculateXOffsetShift();
foreach (TextSpan textSpan in runs)
{
TextRun run = textSpan.Value;
if (run is InlineObject)
{
InlineObject inlineObject = run as InlineObject;
// Disconnect visual from its old parent, if necessary.
Visual currentParent = VisualTreeHelper.GetParent(inlineObject.Element) as Visual;
if (currentParent != null)
{
ContainerVisual parent = currentParent as ContainerVisual;
Invariant.Assert(parent != null, "parent should always derives from ContainerVisual");
parent.Children.Remove(inlineObject.Element);
}
// Get position of inline object withing the text line.
FlowDirection flowDirection;
Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out flowDirection);
Debug.Assert(DoubleUtil.GreaterThanOrClose(rect.Width, 0), "Negative inline object's width.");
ContainerVisual proxyVisual = new ContainerVisual();
if (inlineObject.Element is FrameworkElement)
{
FlowDirection parentFlowDirection = _owner.FlowDirection;
// Check parent's FlowDirection to determine if mirroring is needed
DependencyObject parent = ((FrameworkElement)inlineObject.Element).Parent;
if(parent != null)
{
parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
}
PtsHelper.UpdateMirroringTransform(_owner.FlowDirection, parentFlowDirection, proxyVisual, rect.Width);
}
vc.Add(proxyVisual);
proxyVisual.Offset = new Vector(lineOffset.X + rect.Left, lineOffset.Y + rect.Top);
proxyVisual.Children.Add(inlineObject.Element);
// Combine text line offset (relative to the Text control) with inline object
// offset (relative to the line) and set transorm on the visual. Trailing spaces
// shift is not added here because it is returned by GetBoundsFromPosition
inlineObject.Element.Arrange(new Rect(inlineObject.Element.DesiredSize));
}
// Do not use TextRun.Length, because it gives total length of the run.
// So, if the run is broken between lines, it gives incorrect value.
// Use length of the TextSpan instead, which gives the correct length here.
runDcp += textSpan.Length;
}
}
// ------------------------------------------------------------------
// Find out if there are any inline objects.
// -----------------------------------------------------------------
internal override bool HasInlineObjects()
{
bool hasInlineObjects = false;
IList> runs = _line.GetTextRunSpans();
Debug.Assert(runs != null, "Cannot retrieve runs collection.");
foreach (TextSpan textSpan in runs)
{
if (textSpan.Value is InlineObject)
{
hasInlineObjects = true;
break;
}
}
return hasInlineObjects;
}
// ------------------------------------------------------------------
// Hit tests to the correct ContentElement within the line.
//
// offset - offset within the line.
//
// Returns: ContentElement which has been hit.
// -----------------------------------------------------------------
internal override IInputElement InputHitTest(double offset)
{
TextContainer tree;
DependencyObject element;
CharacterHit charHit;
TextPointer position;
TextPointerContext type = TextPointerContext.None;
element = null;
// We can only support hittesting text elements in a TextContainer.
// If the TextContainer is not a TextContainer, return null which higher up the stack
// will be converted into a reference to the control itself.
tree = _owner.TextContainer as TextContainer;
// Adjusted offset for shift due to trailing spaces rendering
double delta = CalculateXOffsetShift();
if (tree != null)
{
if (_line.HasOverflowed && _owner.ParagraphProperties.TextTrimming != TextTrimming.None)
{
// We should not shift offset in this case
Invariant.Assert(DoubleUtil.AreClose(delta, 0));
System.Windows.Media.TextFormatting.TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties));
Invariant.Assert(line.HasCollapsed, "Line has not been collapsed");
// Get TextPointer from specified distance.
charHit = line.GetCharacterHitFromDistance(offset);
}
else
{
charHit = _line.GetCharacterHitFromDistance(offset - delta);
}
position = new TextPointer(_owner.ContentStart, CalcPositionOffset(charHit), LogicalDirection.Forward);
if (position != null)
{
if (charHit.TrailingLength == 0)
{
// Start of character. Look forward
type = position.GetPointerContext(LogicalDirection.Forward);
}
else
{
// End of character. Look backward
type = position.GetPointerContext(LogicalDirection.Backward);
}
// Get element only for Text & Start/End element, for all other positions
// return null (it means that the line owner has been hit).
if (type == TextPointerContext.Text || type == TextPointerContext.ElementEnd)
{
element = position.Parent as TextElement;
}
else if (type == TextPointerContext.ElementStart)
{
element = position.GetAdjacentElementFromOuterPosition(LogicalDirection.Forward);
}
}
}
return element as IInputElement;
}
#endregion Internal Methods
//-------------------------------------------------------------------
//
// Private Methods
//
//-------------------------------------------------------------------
#region Private Methods
// ------------------------------------------------------------------
// Fetch the next run at text position.
//
// position - current position in the text array
// -----------------------------------------------------------------
private TextRun HandleText(StaticTextPointer position)
{
DependencyObject element;
StaticTextPointer endOfRunPosition;
Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text, "TextPointer does not point to characters.");
if (position.Parent != null)
{
element = position.Parent;
}
else
{
element = _owner;
}
// Extract the aggregated properties into something that the textrun can use.
//
TextRunProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */);
// Calculate the end of the run by finding either:
// a) the next intersection of highlight ranges, or
// b) the natural end of this textrun
endOfRunPosition = _owner.Highlights.GetNextPropertyChangePosition(position, LogicalDirection.Forward);
// Clamp the text run at an arbitrary limit, so we don't make
// an unbounded allocation.
if (position.GetOffsetToPosition(endOfRunPosition) > 4096)
{
endOfRunPosition = position.CreatePointer(4096);
}
// Get character buffer for the text run.
char[] textBuffer = new char[position.GetOffsetToPosition(endOfRunPosition)];
// Copy characters from text run into buffer. Note the actual number of characters copied,
// which may be different than the buffer's length. Buffer length only specifies the maximum
// number of characters
int charactersCopied = position.GetTextInRun(LogicalDirection.Forward, textBuffer, 0, textBuffer.Length);
// Create text run, using characters copied as length
return new TextCharacters(textBuffer, 0, charactersCopied, textProps);
}
// ------------------------------------------------------------------
// Fetch the next run at element open edge position.
//
// position - current position in the text array
// ------------------------------------------------------------------
private TextRun HandleElementStartEdge(StaticTextPointer position)
{
Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart, "TextPointer does not point to element start edge.");
//
TextRun run = null;
TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward);
Debug.Assert(element != null, "Cannot use ITextContainer that does not provide TextElement instances.");
if (element is LineBreak)
{
run = new TextEndOfLine(_elementEdgeCharacterLength * 2);
}
else if (element.IsEmpty)
{
// Empty TextElement should affect line metrics.
// TextFormatter does not support this feature right now, so as workaround
// TextRun with ZERO WIDTH NO-BREAK SPACE is used.
TextRunProperties textProps = new TextProperties(element, position, false /* inline objects */, true /* get background */);
char[] textBuffer = new char[_elementEdgeCharacterLength * 2];
textBuffer[0] = (char)0xFEFF;
textBuffer[1] = (char)0xFEFF;
run = new TextCharacters(textBuffer, 0, textBuffer.Length, textProps);
}
else
{
Inline inline = element as Inline;
if (inline == null)
{
run = new TextHidden(_elementEdgeCharacterLength);
}
else
{
DependencyObject parent = inline.Parent;
FlowDirection inlineFlowDirection = inline.FlowDirection;
FlowDirection parentFlowDirection = inlineFlowDirection;
if(parent != null)
{
parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
}
TextDecorationCollection inlineTextDecorations = DynamicPropertyReader.GetTextDecorations(inline);
if (inlineFlowDirection != parentFlowDirection)
{
// Inline's flow direction is different from its parent. Need to create new TextSpanModifier with flow direction
if (inlineTextDecorations == null || inlineTextDecorations.Count == 0)
{
run = new TextSpanModifier(
_elementEdgeCharacterLength,
null,
null,
inlineFlowDirection
);
}
else
{
run = new TextSpanModifier(
_elementEdgeCharacterLength,
inlineTextDecorations,
inline.Foreground,
inlineFlowDirection
);
}
}
else
{
if (inlineTextDecorations == null || inlineTextDecorations.Count == 0)
{
run = new TextHidden(_elementEdgeCharacterLength);
}
else
{
run = new TextSpanModifier(
_elementEdgeCharacterLength,
inlineTextDecorations,
inline.Foreground
);
}
}
}
}
return run;
}
// -----------------------------------------------------------------
// Fetch the next run at element close edge position.
//
// position - current position in the text array
// ------------------------------------------------------------------
private TextRun HandleElementEndEdge(StaticTextPointer position)
{
Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd, "TextPointer does not point to element end edge.");
TextRun run = null;
TextElement element = (TextElement)position.GetAdjacentElement(LogicalDirection.Forward);
Debug.Assert(element != null, "Element should be here.");
Inline inline = element as Inline;
if (inline == null)
{
run = new TextHidden(_elementEdgeCharacterLength);
}
else
{
DependencyObject parent = inline.Parent;
FlowDirection parentFlowDirection = inline.FlowDirection;
if(parent != null)
{
parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
}
if (inline.FlowDirection != parentFlowDirection)
{
run = new TextEndOfSegment(_elementEdgeCharacterLength);
}
else
{
TextDecorationCollection textDecorations = DynamicPropertyReader.GetTextDecorations(inline);
if (textDecorations == null || textDecorations.Count == 0)
{
// (2) End of inline element, hide CloseEdge character and continue
run = new TextHidden(_elementEdgeCharacterLength);
}
else
{
run = new TextEndOfSegment(_elementEdgeCharacterLength);
}
}
}
return run;
}
// -----------------------------------------------------------------
// Fetch the next run at UIElment position.
//
// position - current position in the text array
// dcp - current position in the text array
// -----------------------------------------------------------------
private TextRun HandleInlineObject(StaticTextPointer position, int dcp)
{
Debug.Assert(position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.EmbeddedElement, "TextPointer does not point to embedded object.");
TextRun run = null;
DependencyObject element = position.GetAdjacentElement(LogicalDirection.Forward) as DependencyObject;
if (element is UIElement)
{
//
TextRunProperties textProps = new TextProperties(element, position, true /* inline objects */, true /* get background */);
// Create object run.
run = new InlineObject(dcp, TextContainerHelper.EmbeddedObjectLength, (UIElement)element, textProps, _owner);
}
else
{
// If the embedded object is of an unknown type (not UIElement),
// treat it as element edge.
run = HandleElementEndEdge(position);
}
return run;
}
///
/// Calculates the offset for the corresponding TextPointer from a CharacterHit
///
///
/// This is necessary to ensure that we don't try to create a position at an offset greater than TextContainer's symbol count.
/// This may happen when a line is collapsed with ellipsis and we hit-test at the trailing edge of ellipsis, the trailing length
/// returned for the CharacterHit is the length of all collapsed characters, including the synthetic EOP. If we try to
/// create a position at this trailing length we can exceed TextContainer's symbol count.
///
private int CalcPositionOffset(CharacterHit charHit)
{
int offset = charHit.FirstCharacterIndex + charHit.TrailingLength;
if (this.EndOfParagraph)
{
offset = Math.Min(_dcp + this.Length, offset);
}
return offset;
}
#endregion Private methods
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private Fields
// -----------------------------------------------------------------
// Element edge character length.
// ------------------------------------------------------------------
private static int _elementEdgeCharacterLength = 1;
#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
- DataGridViewLayoutData.cs
- ManifestResourceInfo.cs
- RemoteCryptoRsaServiceProvider.cs
- MachineKeySection.cs
- NumericPagerField.cs
- XmlNodeChangedEventManager.cs
- FixedBufferAttribute.cs
- PropertyFilter.cs
- TemplateKey.cs
- ContentDisposition.cs
- NetTcpBindingCollectionElement.cs
- HtmlShimManager.cs
- StructuredProperty.cs
- ToolBarButton.cs
- WebPartEditorCancelVerb.cs
- DataTableReader.cs
- TypeExtension.cs
- SqlHelper.cs
- CompModSwitches.cs
- AssertSection.cs
- StaticFileHandler.cs
- ShaderRenderModeValidation.cs
- ExpressionEditorAttribute.cs
- OdbcReferenceCollection.cs
- Help.cs
- SiteMembershipCondition.cs
- RectangleHotSpot.cs
- TcpConnectionPoolSettingsElement.cs
- UdpDiscoveryEndpointProvider.cs
- HashMembershipCondition.cs
- HostSecurityManager.cs
- SymDocumentType.cs
- MergablePropertyAttribute.cs
- DependentList.cs
- ActivityDesignerHighlighter.cs
- ButtonFlatAdapter.cs
- QueryRewriter.cs
- DataGridViewSelectedCellsAccessibleObject.cs
- LinqDataSourceContextData.cs
- PrintPreviewControl.cs
- HTTPNotFoundHandler.cs
- ADMembershipProvider.cs
- CTreeGenerator.cs
- SmiEventSink_Default.cs
- AmbientLight.cs
- IndentTextWriter.cs
- DynamicUpdateCommand.cs
- MonitoringDescriptionAttribute.cs
- CollectionsUtil.cs
- SmiMetaDataProperty.cs
- DataGridToolTip.cs
- SqlRowUpdatingEvent.cs
- ProjectionCamera.cs
- FontFaceLayoutInfo.cs
- DataGridView.cs
- SystemIPv6InterfaceProperties.cs
- WindowsFormsHost.cs
- Attachment.cs
- XmlSchemaAttribute.cs
- DebugView.cs
- ExtensionSimplifierMarkupObject.cs
- TimeoutHelper.cs
- IdentityModelStringsVersion1.cs
- columnmapkeybuilder.cs
- FieldNameLookup.cs
- QueryOperatorEnumerator.cs
- X509Certificate.cs
- XmlTypeAttribute.cs
- CodeTypeDeclarationCollection.cs
- WindowsButton.cs
- MouseGestureConverter.cs
- ButtonPopupAdapter.cs
- TextEditorThreadLocalStore.cs
- MsmqHostedTransportConfiguration.cs
- SafeUserTokenHandle.cs
- WebHttpBindingElement.cs
- ReachDocumentReferenceSerializer.cs
- HtmlInputImage.cs
- BulletDecorator.cs
- Merger.cs
- XmlSchemaNotation.cs
- InitializerFacet.cs
- HttpRuntimeSection.cs
- XmlMapping.cs
- PopupEventArgs.cs
- QilXmlWriter.cs
- LoadMessageLogger.cs
- _SafeNetHandles.cs
- XamlStream.cs
- IisTraceWebEventProvider.cs
- SessionStateModule.cs
- SqlDataRecord.cs
- CompoundFileStorageReference.cs
- ConnectivityStatus.cs
- FaultHandlingFilter.cs
- BaseTemplateBuildProvider.cs
- TagMapInfo.cs
- BitVector32.cs
- RelatedPropertyManager.cs
- CodeDOMProvider.cs