ComplexLine.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / 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 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.
//---------------------------------------------------------------------------- 
//
// 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 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK