TextBoxLine.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / documents / TextBoxLine.cs / 1305600 / TextBoxLine.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: TextBoxLine.cs 
//
// Description: TextLine wrapper used by TextBoxView. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Globalization;
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal;
using MS.Internal.PtsHost;
using MS.Internal.Text; 

namespace System.Windows.Controls 
{ 
    /// 
    /// TextLine wrapper used by TextBoxView. 
    /// 
    internal class TextBoxLine : TextSource, IDisposable
    {
        //------------------------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-------------------------------------------------------------------
 
        #region Constructors

        /// 
        /// Constructor. 
        /// 
        /// Owner of the line. 
        internal TextBoxLine(TextBoxView owner) 
        {
            _owner = owner; 
        }

        #endregion Constructors
 
        // ------------------------------------------------------------------
        // 
        //  Public Methods 
        //
        // ----------------------------------------------------------------- 

        #region Public Methods

        ///  
        /// Free all resources associated with the line. Prepare it for reuse.
        ///  
        public void Dispose() 
        {
            // Dispose text line 
            if (_line != null)
            {
                _line.Dispose();
                _line = null; 
            }
            GC.SuppressFinalize(this); 
        } 

        ///  
        /// Get a text run at specified text source position.
        /// 
        public override TextRun GetTextRun(int dcp)
        { 
            TextRun run = null;
            StaticTextPointer position = _owner.Host.TextContainer.CreateStaticPointerAtOffset(dcp); 
 
            switch (position.GetPointerContext(LogicalDirection.Forward))
            { 
                case TextPointerContext.Text:
                    run = HandleText(position);
                    break;
 
                case TextPointerContext.None:
                    run = new TextEndOfParagraph(_syntheticCharacterLength); 
                    break; 

                case TextPointerContext.ElementStart: 
                case TextPointerContext.ElementEnd:
                case TextPointerContext.EmbeddedElement:
                default:
                    Invariant.Assert(false, "Unsupported position type."); 
                    break;
            } 
            Invariant.Assert(run != null, "TextRun has not been created."); 
            Invariant.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) 
        {
            CharacterBufferRange precedingText = CharacterBufferRange.Empty; 
            CultureInfo culture = null;

            if (dcp > 0)
            { 
                // Create TextPointer at dcp
                ITextPointer position = _owner.Host.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward); 
 
                // Return text in run. If it is at start of TextContainer this will return an empty string.
                // Typically the caller requires just the preceding character.  Worst case is the entire 
                // preceding sentence, which we approximate with a 128 char limit.
                int runLength = Math.Min(128, position.GetTextRunLength(LogicalDirection.Backward));
                char []text = new char[runLength];
                position.GetTextInRun(LogicalDirection.Backward, text, 0, runLength); 

                precedingText = new CharacterBufferRange(text, 0, runLength); 
                culture = DynamicPropertyReader.GetCultureInfo((Control)_owner.Host); 
            }
 
            return new TextSpan(
                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 Public Methods 
 
        //--------------------------------------------------------------------
        // 
        //  Internal Methods
        //
        //--------------------------------------------------------------------
 
        #region Internal Methods
 
        ///  
        /// Create and format text line.
        ///  
        /// First character position for the line.
        /// Width to pass to LS formatter.
        /// Line wrapping width.
        /// Line's properties. 
        /// Run cache.
        /// Text formatter. 
        ///  
        /// formatWidth/paragraphWidth is an attempt to work around bug 114719.
        /// Unfortunately, Line Services cannot guarantee that once a line 
        /// has been measured, measuring the same content with the actual line
        /// width will produce the same line.
        ///
        /// For example, suppose we format dcp 0 with paragraphWidth = 100. 
        /// Suppose this results in a line from dcp 0 - 10, with width = 95.
        /// 
        /// We would expect that a call to FormatLine with dcp = 0, 
        /// paragraphWidth = 95 would result in the same 10 char line.
        /// But in practice it might return a 9 char line. 
        ///
        /// The workaround is to pass in an explicit formatting width across
        /// multiple calls, even if the paragraphWidth changes.
        ///  
        internal void Format(int dcp, double formatWidth, double paragraphWidth, LineProperties lineProperties, TextRunCache textRunCache, TextFormatter formatter)
        { 
            _lineProperties = lineProperties; 
            _dcp = dcp;
            _paragraphWidth = paragraphWidth; 

            // We must ignore TextAlignment here since formatWidth does not
            // necessarilly equal paragraphWidth.  We'll adjust on later calls.
            lineProperties.IgnoreTextAlignment = (lineProperties.TextAlignment != TextAlignment.Justify); 
            try
            { 
                _line = formatter.FormatLine(this, dcp, formatWidth, lineProperties, null, textRunCache); 
            }
            finally 
            {
                lineProperties.IgnoreTextAlignment = false;
            }
        } 

        ///  
        /// Create and return visual node for the line. 
        /// 
        internal TextBoxLineDrawingVisual CreateVisual() 
        {
            TextBoxLineDrawingVisual visual = new TextBoxLineDrawingVisual();

            // Calculate shift in line offset to render trailing spaces or avoid clipping text. 
            double delta = CalculateXOffsetShift();
            DrawingContext ctx = visual.RenderOpen(); 
            _line.Draw(ctx, new Point(delta, 0), ((_lineProperties.FlowDirection == FlowDirection.RightToLeft) ? InvertAxes.Horizontal : InvertAxes.None)); 
            ctx.Close();
 
            return visual;
        }

        ///  
        /// Retrieve bounds of an object/character at specified text position.
        ///  
        /// position of an object/character 
        /// flow direction of object/character
        /// Bounds of an object/character 
        internal Rect GetBoundsFromTextPosition(int characterIndex, out FlowDirection flowDirection)
        {
            return GetBoundsFromPosition(characterIndex, 1, out flowDirection);
        } 

        ///  
        /// Returns a collection of rectangles (Rect) that form the bounds of the region 
        /// specified between the start and end points.
        ///  
        /// Starting point of the region
        /// Length in characters of the region
        /// Offset of line in x direction, to be added to line bounds
        /// Offset of line in y direction, to be added to line bounds 
        /// 
        /// This function calls GetTextBounds for the line, and then checks if there are 
        /// text run bounds. If they exist, it uses those as the bounding rectangles. If not, 
        /// it returns the rectangle for the first (and only) element of the text bounds.
        ///  
        internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffset)
        {
            List rectangles = new List();
 
            // Adjust x offset for trailing spaces
            double delta = CalculateXOffsetShift(); 
            double adjustedXOffset = xOffset + delta; 

            IList textBounds = _line.GetTextBounds(cp, cch); 
            Invariant.Assert(textBounds.Count > 0);

            for (int boundIndex = 0; boundIndex < textBounds.Count; boundIndex++)
            { 
                Rect rect = textBounds[boundIndex].Rectangle;
                rect.X += adjustedXOffset; 
                rect.Y += yOffset; 
                rectangles.Add(rect);
            } 
            return rectangles;
        }

        ///  
        /// Retrieve text position index from the distance.
        ///  
        /// distance relative to the beginning of the line 
        /// Text position index
        internal CharacterHit GetTextPositionFromDistance(double distance) 
        {
            // Adjust distance to account for a line shift due to rendering of trailing spaces
            double delta = CalculateXOffsetShift();
            return _line.GetCharacterHitFromDistance(distance - delta); 
        }
 
        ///  
        /// Retrieve text position for next caret position.
        ///  
        /// CharacterHit for current position
        /// Text position index
        internal CharacterHit GetNextCaretCharacterHit(CharacterHit index)
        { 
            return _line.GetNextCaretCharacterHit(index);
        } 
 
        /// 
        /// Retrieve text position for previous caret position. 
        /// 
        /// CharacterHit for current position
        /// Text position index
        internal CharacterHit GetPreviousCaretCharacterHit(CharacterHit index) 
        {
            return _line.GetPreviousCaretCharacterHit(index); 
        } 

        ///  
        /// Retrieve text position for backspace caret position.
        /// 
        /// CharacterHit for current position
        /// Text position index 
        internal CharacterHit GetBackspaceCaretCharacterHit(CharacterHit index)
        { 
            return _line.GetBackspaceCaretCharacterHit(index); 
        }
 
        /// 
        /// Returns true of char hit is at caret unit boundary.
        /// 
        ///  
        /// CharacterHit to be tested.
        ///  
        internal bool IsAtCaretCharacterHit(CharacterHit charHit) 
        {
            return _line.IsAtCaretCharacterHit(charHit, _dcp); 
        }

        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        // 
        //  Internal Properties 
        //
        //-------------------------------------------------------------------- 

        #region Internal Properties

        ///  
        /// Calculated width of the line.
        ///  
        internal double Width 
        {
            get 
            {
                if (IsWidthAdjusted)
                {
                    // Trailing spaces add to width 
                    return _line.WidthIncludingTrailingWhitespace;
                } 
                else 
                {
                    return _line.Width; 
                }
            }
        }
 
        /// 
        /// Height of the line; line advance distance. 
        ///  
        internal double Height { get { return _line.Height; } }
 
        /// 
        /// Is this the last line of the paragraph?
        /// 
        internal bool EndOfParagraph 
        {
            get 
            { 
                // If there are no Newline characters, it is not the end of paragraph.
                if (_line.NewlineLength == 0) { return false; } 
                // Since there are Newline characters in the line, do more expensive and
                // accurate check.
                IList> runs = _line.GetTextRunSpans();
                return (((TextSpan)runs[runs.Count-1]).Value is TextEndOfParagraph); 
            }
        } 
 
        /// 
        /// Length of the line excluding any synthetic characters. 
        /// 
        internal int Length
        {
            get { return _line.Length - (EndOfParagraph ? 1 : 0); } 
        }
 
        ///  
        /// Length of the line excluding any synthetic characters and line breaks.
        ///  
        internal int ContentLength { get { return _line.Length - _line.NewlineLength; } }

        /// 
        /// True if line ends in hard line break. 
        /// 
        internal bool HasLineBreak 
        { 
            get
            { 
                return (_line.NewlineLength > 0);
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------------------- 
        //
        //  Private Methods 
        //
        //-------------------------------------------------------------------

        #region Private Methods 

        ///  
        /// Fetch the next run at text position. 
        /// 
        private TextRun HandleText(StaticTextPointer position) 
        {
            // Calculate the end of the run by finding either:
            //      a) the next intersection of highlight ranges, or
            //      b) the natural end of this textrun 
            StaticTextPointer endOfRunPosition = _owner.Host.TextContainer.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);
            }
 
            // Factor in any speller error squiggles on the run.
            TextDecorationCollection highlightDecorations = position.TextContainer.Highlights.GetHighlightValue(position, LogicalDirection.Forward, typeof(SpellerHighlightLayer)) as TextDecorationCollection; 
            TextRunProperties properties; 

            if (highlightDecorations == null) 
            {
                properties = _lineProperties.DefaultTextRunProperties;
            }
            else 
            {
                if (_spellerErrorProperties == null) 
                { 
                    _spellerErrorProperties = new TextProperties((TextProperties)_lineProperties.DefaultTextRunProperties, highlightDecorations);
                } 
                properties = _spellerErrorProperties;
            }

            // Get character buffer for the text run. 
            char[] textBuffer = new char[position.GetOffsetToPosition(endOfRunPosition)];
 
            // Copy characters from text run into buffer. Since we are dealing with plain text content, 
            // we expect to get all the characters from position to endOfRunPosition.
            int charactersCopied = position.GetTextInRun(LogicalDirection.Forward, textBuffer, 0, textBuffer.Length); 
            Invariant.Assert(charactersCopied == textBuffer.Length);

            // Create text run, using characters copied as length
            return new TextCharacters(textBuffer, 0, charactersCopied, properties); 
        }
 
        ///  
        /// Retrieve bounds of an object/character at specified text index.
        ///  
        /// character index of an object/character
        /// number of positions occupied by object/character
        /// flow direction of object/character
        /// Bounds of an object/character 
        private Rect GetBoundsFromPosition(int cp, int cch, out FlowDirection flowDirection)
        { 
            Rect rect; 

            // Adjust x offset for trailing spaces 
            double delta = CalculateXOffsetShift();
            IList textBounds = _line.GetTextBounds(cp, cch);
            Invariant.Assert(textBounds != null && textBounds.Count == 1, "Expecting exactly one TextBounds for a single text position.");
 
            IList runBounds = textBounds[0].TextRunBounds;
            if (runBounds != null) 
            { 
                Invariant.Assert(runBounds.Count == 1, "Expecting exactly one TextRunBounds for a single text position.");
                rect = runBounds[0].Rectangle; 
            }
            else
            {
                rect = textBounds[0].Rectangle; 
            }
 
            rect.X += delta; 
            flowDirection = textBounds[0].FlowDirection;
            return rect; 
        }

        /// 
        /// Returns amount of shift for X-offset to render trailing spaces 
        /// and TextAlignment offset.
        ///  
        private double CalculateXOffsetShift() 
        {
            double xOffset = 0; 

            if (_lineProperties.TextAlignmentInternal == TextAlignment.Right)
            {
                xOffset = _paragraphWidth - _line.Width; 
            }
            else if (_lineProperties.TextAlignmentInternal == TextAlignment.Center) 
            { 
                xOffset = (_paragraphWidth - _line.Width) / 2;
            } 

            if (IsXOffsetAdjusted)
            {
                if (_lineProperties.TextAlignmentInternal == TextAlignment.Center) 
                {
                    // Return trailing spaces length divided by two so line remains centered. 
                    xOffset += (_line.Width - _line.WidthIncludingTrailingWhitespace) / 2; 
                }
                else 
                {
                    xOffset += (_line.Width - _line.WidthIncludingTrailingWhitespace);
                }
            } 

            return xOffset; 
        } 

        #endregion Private Methods 

        //-------------------------------------------------------------------
        //
        //  Private Properites 
        //
        //-------------------------------------------------------------------- 
 
        #region Private Properties
 
        /// 
        /// True if line's X-offset needs adjustment to render trailing spaces.
        /// 
        private bool IsXOffsetAdjusted 
        {
            get 
            { 
                return ((_lineProperties.TextAlignmentInternal == TextAlignment.Right || _lineProperties.TextAlignmentInternal == TextAlignment.Center) && IsWidthAdjusted);
            } 
        }

        /// 
        /// True if line's width is adjusted to include trailing spaces. For right and center alignment we need to 
        /// adjust line offset as well, but for left alignment we need to only make a width asjustment.
        ///  
        private bool IsWidthAdjusted 
        {
            get 
            {
                // Trailing spaces rendered only around hard breaks
                return (HasLineBreak || EndOfParagraph);
            } 
        }
 
        #endregion Private Properties 

        //------------------------------------------------------------------- 
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------- 

        #region Private Fields 
 
        /// 
        /// Owner of the line. 
        /// 
        private readonly TextBoxView _owner;

        ///  
        /// Cached text line.
        ///  
        private TextLine _line; 

        ///  
        /// Index of the first character in the line.
        /// 
        private int _dcp;
 
        /// 
        /// Properties of the line. 
        ///  
        private LineProperties _lineProperties;
 
        /// 
        /// Properties of the line when covered by a spelling error squiggle.
        /// 
        private TextProperties _spellerErrorProperties; 

        ///  
        /// Width of the enclosing paragraph, used for TextAlignment calculations. 
        /// 
        private double _paragraphWidth; 

        /// 
        /// TextEndOfParagraph character count.
        ///  
        private const int _syntheticCharacterLength = 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: TextBoxLine.cs 
//
// Description: TextLine wrapper used by TextBoxView. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Globalization;
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal;
using MS.Internal.PtsHost;
using MS.Internal.Text; 

namespace System.Windows.Controls 
{ 
    /// 
    /// TextLine wrapper used by TextBoxView. 
    /// 
    internal class TextBoxLine : TextSource, IDisposable
    {
        //------------------------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-------------------------------------------------------------------
 
        #region Constructors

        /// 
        /// Constructor. 
        /// 
        /// Owner of the line. 
        internal TextBoxLine(TextBoxView owner) 
        {
            _owner = owner; 
        }

        #endregion Constructors
 
        // ------------------------------------------------------------------
        // 
        //  Public Methods 
        //
        // ----------------------------------------------------------------- 

        #region Public Methods

        ///  
        /// Free all resources associated with the line. Prepare it for reuse.
        ///  
        public void Dispose() 
        {
            // Dispose text line 
            if (_line != null)
            {
                _line.Dispose();
                _line = null; 
            }
            GC.SuppressFinalize(this); 
        } 

        ///  
        /// Get a text run at specified text source position.
        /// 
        public override TextRun GetTextRun(int dcp)
        { 
            TextRun run = null;
            StaticTextPointer position = _owner.Host.TextContainer.CreateStaticPointerAtOffset(dcp); 
 
            switch (position.GetPointerContext(LogicalDirection.Forward))
            { 
                case TextPointerContext.Text:
                    run = HandleText(position);
                    break;
 
                case TextPointerContext.None:
                    run = new TextEndOfParagraph(_syntheticCharacterLength); 
                    break; 

                case TextPointerContext.ElementStart: 
                case TextPointerContext.ElementEnd:
                case TextPointerContext.EmbeddedElement:
                default:
                    Invariant.Assert(false, "Unsupported position type."); 
                    break;
            } 
            Invariant.Assert(run != null, "TextRun has not been created."); 
            Invariant.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) 
        {
            CharacterBufferRange precedingText = CharacterBufferRange.Empty; 
            CultureInfo culture = null;

            if (dcp > 0)
            { 
                // Create TextPointer at dcp
                ITextPointer position = _owner.Host.TextContainer.CreatePointerAtOffset(dcp, LogicalDirection.Backward); 
 
                // Return text in run. If it is at start of TextContainer this will return an empty string.
                // Typically the caller requires just the preceding character.  Worst case is the entire 
                // preceding sentence, which we approximate with a 128 char limit.
                int runLength = Math.Min(128, position.GetTextRunLength(LogicalDirection.Backward));
                char []text = new char[runLength];
                position.GetTextInRun(LogicalDirection.Backward, text, 0, runLength); 

                precedingText = new CharacterBufferRange(text, 0, runLength); 
                culture = DynamicPropertyReader.GetCultureInfo((Control)_owner.Host); 
            }
 
            return new TextSpan(
                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 Public Methods 
 
        //--------------------------------------------------------------------
        // 
        //  Internal Methods
        //
        //--------------------------------------------------------------------
 
        #region Internal Methods
 
        ///  
        /// Create and format text line.
        ///  
        /// First character position for the line.
        /// Width to pass to LS formatter.
        /// Line wrapping width.
        /// Line's properties. 
        /// Run cache.
        /// Text formatter. 
        ///  
        /// formatWidth/paragraphWidth is an attempt to work around bug 114719.
        /// Unfortunately, Line Services cannot guarantee that once a line 
        /// has been measured, measuring the same content with the actual line
        /// width will produce the same line.
        ///
        /// For example, suppose we format dcp 0 with paragraphWidth = 100. 
        /// Suppose this results in a line from dcp 0 - 10, with width = 95.
        /// 
        /// We would expect that a call to FormatLine with dcp = 0, 
        /// paragraphWidth = 95 would result in the same 10 char line.
        /// But in practice it might return a 9 char line. 
        ///
        /// The workaround is to pass in an explicit formatting width across
        /// multiple calls, even if the paragraphWidth changes.
        ///  
        internal void Format(int dcp, double formatWidth, double paragraphWidth, LineProperties lineProperties, TextRunCache textRunCache, TextFormatter formatter)
        { 
            _lineProperties = lineProperties; 
            _dcp = dcp;
            _paragraphWidth = paragraphWidth; 

            // We must ignore TextAlignment here since formatWidth does not
            // necessarilly equal paragraphWidth.  We'll adjust on later calls.
            lineProperties.IgnoreTextAlignment = (lineProperties.TextAlignment != TextAlignment.Justify); 
            try
            { 
                _line = formatter.FormatLine(this, dcp, formatWidth, lineProperties, null, textRunCache); 
            }
            finally 
            {
                lineProperties.IgnoreTextAlignment = false;
            }
        } 

        ///  
        /// Create and return visual node for the line. 
        /// 
        internal TextBoxLineDrawingVisual CreateVisual() 
        {
            TextBoxLineDrawingVisual visual = new TextBoxLineDrawingVisual();

            // Calculate shift in line offset to render trailing spaces or avoid clipping text. 
            double delta = CalculateXOffsetShift();
            DrawingContext ctx = visual.RenderOpen(); 
            _line.Draw(ctx, new Point(delta, 0), ((_lineProperties.FlowDirection == FlowDirection.RightToLeft) ? InvertAxes.Horizontal : InvertAxes.None)); 
            ctx.Close();
 
            return visual;
        }

        ///  
        /// Retrieve bounds of an object/character at specified text position.
        ///  
        /// position of an object/character 
        /// flow direction of object/character
        /// Bounds of an object/character 
        internal Rect GetBoundsFromTextPosition(int characterIndex, out FlowDirection flowDirection)
        {
            return GetBoundsFromPosition(characterIndex, 1, out flowDirection);
        } 

        ///  
        /// Returns a collection of rectangles (Rect) that form the bounds of the region 
        /// specified between the start and end points.
        ///  
        /// Starting point of the region
        /// Length in characters of the region
        /// Offset of line in x direction, to be added to line bounds
        /// Offset of line in y direction, to be added to line bounds 
        /// 
        /// This function calls GetTextBounds for the line, and then checks if there are 
        /// text run bounds. If they exist, it uses those as the bounding rectangles. If not, 
        /// it returns the rectangle for the first (and only) element of the text bounds.
        ///  
        internal List GetRangeBounds(int cp, int cch, double xOffset, double yOffset)
        {
            List rectangles = new List();
 
            // Adjust x offset for trailing spaces
            double delta = CalculateXOffsetShift(); 
            double adjustedXOffset = xOffset + delta; 

            IList textBounds = _line.GetTextBounds(cp, cch); 
            Invariant.Assert(textBounds.Count > 0);

            for (int boundIndex = 0; boundIndex < textBounds.Count; boundIndex++)
            { 
                Rect rect = textBounds[boundIndex].Rectangle;
                rect.X += adjustedXOffset; 
                rect.Y += yOffset; 
                rectangles.Add(rect);
            } 
            return rectangles;
        }

        ///  
        /// Retrieve text position index from the distance.
        ///  
        /// distance relative to the beginning of the line 
        /// Text position index
        internal CharacterHit GetTextPositionFromDistance(double distance) 
        {
            // Adjust distance to account for a line shift due to rendering of trailing spaces
            double delta = CalculateXOffsetShift();
            return _line.GetCharacterHitFromDistance(distance - delta); 
        }
 
        ///  
        /// Retrieve text position for next caret position.
        ///  
        /// CharacterHit for current position
        /// Text position index
        internal CharacterHit GetNextCaretCharacterHit(CharacterHit index)
        { 
            return _line.GetNextCaretCharacterHit(index);
        } 
 
        /// 
        /// Retrieve text position for previous caret position. 
        /// 
        /// CharacterHit for current position
        /// Text position index
        internal CharacterHit GetPreviousCaretCharacterHit(CharacterHit index) 
        {
            return _line.GetPreviousCaretCharacterHit(index); 
        } 

        ///  
        /// Retrieve text position for backspace caret position.
        /// 
        /// CharacterHit for current position
        /// Text position index 
        internal CharacterHit GetBackspaceCaretCharacterHit(CharacterHit index)
        { 
            return _line.GetBackspaceCaretCharacterHit(index); 
        }
 
        /// 
        /// Returns true of char hit is at caret unit boundary.
        /// 
        ///  
        /// CharacterHit to be tested.
        ///  
        internal bool IsAtCaretCharacterHit(CharacterHit charHit) 
        {
            return _line.IsAtCaretCharacterHit(charHit, _dcp); 
        }

        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        // 
        //  Internal Properties 
        //
        //-------------------------------------------------------------------- 

        #region Internal Properties

        ///  
        /// Calculated width of the line.
        ///  
        internal double Width 
        {
            get 
            {
                if (IsWidthAdjusted)
                {
                    // Trailing spaces add to width 
                    return _line.WidthIncludingTrailingWhitespace;
                } 
                else 
                {
                    return _line.Width; 
                }
            }
        }
 
        /// 
        /// Height of the line; line advance distance. 
        ///  
        internal double Height { get { return _line.Height; } }
 
        /// 
        /// Is this the last line of the paragraph?
        /// 
        internal bool EndOfParagraph 
        {
            get 
            { 
                // If there are no Newline characters, it is not the end of paragraph.
                if (_line.NewlineLength == 0) { return false; } 
                // Since there are Newline characters in the line, do more expensive and
                // accurate check.
                IList> runs = _line.GetTextRunSpans();
                return (((TextSpan)runs[runs.Count-1]).Value is TextEndOfParagraph); 
            }
        } 
 
        /// 
        /// Length of the line excluding any synthetic characters. 
        /// 
        internal int Length
        {
            get { return _line.Length - (EndOfParagraph ? 1 : 0); } 
        }
 
        ///  
        /// Length of the line excluding any synthetic characters and line breaks.
        ///  
        internal int ContentLength { get { return _line.Length - _line.NewlineLength; } }

        /// 
        /// True if line ends in hard line break. 
        /// 
        internal bool HasLineBreak 
        { 
            get
            { 
                return (_line.NewlineLength > 0);
            }
        }
 
        #endregion Internal Properties
 
        //------------------------------------------------------------------- 
        //
        //  Private Methods 
        //
        //-------------------------------------------------------------------

        #region Private Methods 

        ///  
        /// Fetch the next run at text position. 
        /// 
        private TextRun HandleText(StaticTextPointer position) 
        {
            // Calculate the end of the run by finding either:
            //      a) the next intersection of highlight ranges, or
            //      b) the natural end of this textrun 
            StaticTextPointer endOfRunPosition = _owner.Host.TextContainer.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);
            }
 
            // Factor in any speller error squiggles on the run.
            TextDecorationCollection highlightDecorations = position.TextContainer.Highlights.GetHighlightValue(position, LogicalDirection.Forward, typeof(SpellerHighlightLayer)) as TextDecorationCollection; 
            TextRunProperties properties; 

            if (highlightDecorations == null) 
            {
                properties = _lineProperties.DefaultTextRunProperties;
            }
            else 
            {
                if (_spellerErrorProperties == null) 
                { 
                    _spellerErrorProperties = new TextProperties((TextProperties)_lineProperties.DefaultTextRunProperties, highlightDecorations);
                } 
                properties = _spellerErrorProperties;
            }

            // Get character buffer for the text run. 
            char[] textBuffer = new char[position.GetOffsetToPosition(endOfRunPosition)];
 
            // Copy characters from text run into buffer. Since we are dealing with plain text content, 
            // we expect to get all the characters from position to endOfRunPosition.
            int charactersCopied = position.GetTextInRun(LogicalDirection.Forward, textBuffer, 0, textBuffer.Length); 
            Invariant.Assert(charactersCopied == textBuffer.Length);

            // Create text run, using characters copied as length
            return new TextCharacters(textBuffer, 0, charactersCopied, properties); 
        }
 
        ///  
        /// Retrieve bounds of an object/character at specified text index.
        ///  
        /// character index of an object/character
        /// number of positions occupied by object/character
        /// flow direction of object/character
        /// Bounds of an object/character 
        private Rect GetBoundsFromPosition(int cp, int cch, out FlowDirection flowDirection)
        { 
            Rect rect; 

            // Adjust x offset for trailing spaces 
            double delta = CalculateXOffsetShift();
            IList textBounds = _line.GetTextBounds(cp, cch);
            Invariant.Assert(textBounds != null && textBounds.Count == 1, "Expecting exactly one TextBounds for a single text position.");
 
            IList runBounds = textBounds[0].TextRunBounds;
            if (runBounds != null) 
            { 
                Invariant.Assert(runBounds.Count == 1, "Expecting exactly one TextRunBounds for a single text position.");
                rect = runBounds[0].Rectangle; 
            }
            else
            {
                rect = textBounds[0].Rectangle; 
            }
 
            rect.X += delta; 
            flowDirection = textBounds[0].FlowDirection;
            return rect; 
        }

        /// 
        /// Returns amount of shift for X-offset to render trailing spaces 
        /// and TextAlignment offset.
        ///  
        private double CalculateXOffsetShift() 
        {
            double xOffset = 0; 

            if (_lineProperties.TextAlignmentInternal == TextAlignment.Right)
            {
                xOffset = _paragraphWidth - _line.Width; 
            }
            else if (_lineProperties.TextAlignmentInternal == TextAlignment.Center) 
            { 
                xOffset = (_paragraphWidth - _line.Width) / 2;
            } 

            if (IsXOffsetAdjusted)
            {
                if (_lineProperties.TextAlignmentInternal == TextAlignment.Center) 
                {
                    // Return trailing spaces length divided by two so line remains centered. 
                    xOffset += (_line.Width - _line.WidthIncludingTrailingWhitespace) / 2; 
                }
                else 
                {
                    xOffset += (_line.Width - _line.WidthIncludingTrailingWhitespace);
                }
            } 

            return xOffset; 
        } 

        #endregion Private Methods 

        //-------------------------------------------------------------------
        //
        //  Private Properites 
        //
        //-------------------------------------------------------------------- 
 
        #region Private Properties
 
        /// 
        /// True if line's X-offset needs adjustment to render trailing spaces.
        /// 
        private bool IsXOffsetAdjusted 
        {
            get 
            { 
                return ((_lineProperties.TextAlignmentInternal == TextAlignment.Right || _lineProperties.TextAlignmentInternal == TextAlignment.Center) && IsWidthAdjusted);
            } 
        }

        /// 
        /// True if line's width is adjusted to include trailing spaces. For right and center alignment we need to 
        /// adjust line offset as well, but for left alignment we need to only make a width asjustment.
        ///  
        private bool IsWidthAdjusted 
        {
            get 
            {
                // Trailing spaces rendered only around hard breaks
                return (HasLineBreak || EndOfParagraph);
            } 
        }
 
        #endregion Private Properties 

        //------------------------------------------------------------------- 
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------- 

        #region Private Fields 
 
        /// 
        /// Owner of the line. 
        /// 
        private readonly TextBoxView _owner;

        ///  
        /// Cached text line.
        ///  
        private TextLine _line; 

        ///  
        /// Index of the first character in the line.
        /// 
        private int _dcp;
 
        /// 
        /// Properties of the line. 
        ///  
        private LineProperties _lineProperties;
 
        /// 
        /// Properties of the line when covered by a spelling error squiggle.
        /// 
        private TextProperties _spellerErrorProperties; 

        ///  
        /// Width of the enclosing paragraph, used for TextAlignment calculations. 
        /// 
        private double _paragraphWidth; 

        /// 
        /// TextEndOfParagraph character count.
        ///  
        private const int _syntheticCharacterLength = 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