Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Text / ComplexLine.cs / 1 / ComplexLine.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: ComplexLine.cs // // Description: Text line formatter. // // History: // 09/10/2003 : grzegorz - 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 TextSpanGetPrecedingText(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. //---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: ComplexLine.cs // // Description: Text line formatter. // // History: // 09/10/2003 : grzegorz - 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 TextSpanGetPrecedingText(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
- InstanceData.cs
- WebServiceClientProxyGenerator.cs
- SqlUtils.cs
- PackWebResponse.cs
- MenuBindingsEditorForm.cs
- FunctionQuery.cs
- GenericWebPart.cs
- Int32CollectionValueSerializer.cs
- DbConnectionPoolCounters.cs
- CacheMemory.cs
- Tuple.cs
- XmlUtil.cs
- DiscoveryClientChannelFactory.cs
- GenericAuthenticationEventArgs.cs
- FileUpload.cs
- TemplateBindingExtensionConverter.cs
- CompositeActivityTypeDescriptor.cs
- XsdDuration.cs
- DeflateEmulationStream.cs
- FixedSOMPageConstructor.cs
- DataGridTablesFactory.cs
- UInt16Storage.cs
- ReadWriteObjectLock.cs
- XmlSchemaComplexContentRestriction.cs
- AttachedPropertyBrowsableWhenAttributePresentAttribute.cs
- DefaultBindingPropertyAttribute.cs
- CursorEditor.cs
- hresults.cs
- DataGridViewLinkCell.cs
- TagElement.cs
- ControlBindingsCollection.cs
- WebHttpBinding.cs
- RadioButton.cs
- HealthMonitoringSectionHelper.cs
- IISMapPath.cs
- EdmTypeAttribute.cs
- DefaultObjectMappingItemCollection.cs
- ButtonRenderer.cs
- DocumentCollection.cs
- EncoderNLS.cs
- CallTemplateAction.cs
- AliasGenerator.cs
- PolicyChain.cs
- SqlReferenceCollection.cs
- PropertyStore.cs
- MsmqInputChannelListenerBase.cs
- HttpCachePolicyBase.cs
- WmlCalendarAdapter.cs
- Vertex.cs
- WebResourceUtil.cs
- SharedStatics.cs
- PageCodeDomTreeGenerator.cs
- XsltArgumentList.cs
- XmlDataLoader.cs
- RelationshipConverter.cs
- KeyValueConfigurationElement.cs
- MdImport.cs
- MasterPageParser.cs
- XmlNamespaceMappingCollection.cs
- DataBoundControlHelper.cs
- ServiceChannelManager.cs
- UserPersonalizationStateInfo.cs
- Int32AnimationUsingKeyFrames.cs
- CacheHelper.cs
- AutoResizedEvent.cs
- SchemaExporter.cs
- InternalSendMessage.cs
- CodeDirectionExpression.cs
- FocusManager.cs
- _FtpControlStream.cs
- BoundsDrawingContextWalker.cs
- messageonlyhwndwrapper.cs
- ForceCopyBuildProvider.cs
- SBCSCodePageEncoding.cs
- HttpClientChannel.cs
- GenericPrincipal.cs
- webbrowsersite.cs
- ExternalException.cs
- AppliesToBehaviorDecisionTable.cs
- ProfileManager.cs
- XmlTextEncoder.cs
- TextTreeUndo.cs
- Pen.cs
- LogicalExpr.cs
- EntityDataSourceWrapperCollection.cs
- FileDialog_Vista_Interop.cs
- MouseEventArgs.cs
- MarshalByValueComponent.cs
- NameTable.cs
- PeerNearMe.cs
- ClientScriptManagerWrapper.cs
- Int32CollectionValueSerializer.cs
- SecurityPolicySection.cs
- PageThemeParser.cs
- LocatorPartList.cs
- ThemeDirectoryCompiler.cs
- AccessText.cs
- OdbcDataReader.cs
- Size.cs
- AutomationElement.cs