CompositionAdorner.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 / System / Windows / Documents / CompositionAdorner.cs / 1407647 / CompositionAdorner.cs

                            //---------------------------------------------------------------------------- 
//
// File: CompositionAdorner.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Composition adorner to render the composition display attribute. 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Documents
{
    using System.Collections; // ArrayList
    using System.Diagnostics; 
    using System.Windows.Media; // Brush, Transform
    using System.Windows.Controls; // TextBox 
    using System.Windows.Threading; // Dispatcher 
    using MS.Win32;             // TextServices
    using MS.Internal; // Invariant 

    internal class CompositionAdorner : Adorner
    {
        //----------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
 
        #region Constructors

        static CompositionAdorner()
        { 
            // Provide a new default value for the composition adorner so that it is not hit-testable.
            IsEnabledProperty.OverrideMetadata(typeof(CompositionAdorner), new FrameworkPropertyMetadata(false)); 
        } 

        ///  
        /// Creates new instance of CompositionAdorner.
        /// 
        /// 
        /// TextView to which this CompositionAdorner is attached as adorner. 
        /// 
        internal CompositionAdorner(ITextView textView) : this(textView, new ArrayList()) 
        { 
        }
 
        /// 
        /// Creates new instance of CompositionAdorner.
        /// 
        ///  
        /// TextView to which this CompositionAdorner is attached as adorner.
        ///  
        ///  
        /// Attribute ranges
        ///  
        internal CompositionAdorner(ITextView textView, ArrayList attributeRanges)
            : base(textView.RenderScope)
        {
            Debug.Assert(textView != null && textView.RenderScope != null); 

            // TextView to which this CompositionAdorner is attached as adorner and it will 
            // als be used for GetRectangleFromTextPosition/GetLineRange 
            _textView = textView;
 
            // Create ArrayList for the composition attribute ranges and composition lines
            _attributeRanges = attributeRanges;
        }
 
        #endregion Constructors
 
        //------------------------------------------------------ 
        //
        //  Public Methods 
        //
        //-----------------------------------------------------

        #region Public Methods 

        ///  
        /// Add a transform so that the composition adorner gets positioned at the correct spot within the text being edited 
        /// 
        ///  
        /// The transform applied to the object the adorner adorns
        /// 
        /// 
        /// Transform to apply to the adorner 
        /// 
        public override GeneralTransform GetDesiredTransform(GeneralTransform transform) 
        { 
            TranslateTransform translation;
            GeneralTransformGroup group = new GeneralTransformGroup(); 

            // Get the matrix transform out, skip all non affine transforms
            Transform t = transform.AffineTransform;
            if (t == null) 
            {
                t = Transform.Identity; 
            } 

            // Translate the adorner to (0, 0) point 
            translation = new TranslateTransform(-(t.Value.OffsetX), -(t.Value.OffsetY));

            group.Children.Add(translation);
 
            if (transform != null)
            { 
                group.Children.Add(transform); 
            }
 
            return group;
        }

        #endregion Public Methods 

        //------------------------------------------------------ 
        // 
        //  Protected Methods
        // 
        //------------------------------------------------------

        #region Protected Methods
 
        /// 
        /// Render override to render the composition adorner here. 
        ///  
        protected override void OnRender(DrawingContext drawingContext)
        { 
            // Get the matrix from AdornedElement to the visual parent to get the transformed
            // start/end point
            Visual parent2d = VisualTreeHelper.GetParent(this.AdornedElement) as Visual;
            if (parent2d == null) 
            {
                return; 
            } 

            GeneralTransform transform = AdornedElement.TransformToAncestor(parent2d); 
            if (transform == null)
            {
                return;
            } 

            // Render the each of the composition string attribute from the attribute ranges. 
            for (int i = 0; i < _attributeRanges.Count; i++) 
            {
                DoubleCollection dashArray; 

                // Get the composition attribute range from the attribute range lists
                AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
 
                // Skip the rendering composition lines if the composition line doesn't exist.
                if (attributeRange.CompositionLines.Count == 0) 
                { 
                    continue;
                } 

                // Set the line bold and squiggle
                bool lineBold = attributeRange.TextServicesDisplayAttribute.IsBoldLine ? true : false;
                bool squiggle = false; 

                // Set the line height and cluse gap value that base on the ratio of text height 
                double height = attributeRange.Height; 
                double lineHeight = height * (lineBold ? BoldLineHeightRatio : NormalLineHeightRatio);
                double clauseGap = height * ClauseGapRatio; 

                // Create Pen for drawing the composition lines with the specified line color
                Pen pen = new Pen(new SolidColorBrush(Colors.Black), lineHeight);
 
                // Set the pen style that based on IME's composition line style
                switch (attributeRange.TextServicesDisplayAttribute.LineStyle) 
                { 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT:
                        // Add the dot length and specify the start/end line cap as the round 
                        dashArray = new DoubleCollection();
                        dashArray.Add(DotLength);
                        dashArray.Add(DotLength);
 
                        pen.DashStyle = new DashStyle(dashArray, 0);
                        pen.DashCap = System.Windows.Media.PenLineCap.Round; 
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
 
                        // Update the line height for the dot line. Dot line will be more thickness than
                        // other line to show it clearly.
                        lineHeight = height * (lineBold ? BoldDotLineHeightRatio : NormalDotLineHeightRatio);
 
                        break;
 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DASH: 
                        double dashLength = height * (lineBold ? BoldDashRatio : NormalDashRatio);
                        double dashGapLength = height * (lineBold ? BoldDashGapRatio : NormalDashGapRatio); 

                        // Add the dash and dash gap legth
                        dashArray = new DoubleCollection();
                        dashArray.Add(dashLength); 
                        dashArray.Add(dashGapLength);
 
                        pen.DashStyle = new DashStyle(dashArray, 0); 
                        pen.DashCap = System.Windows.Media.PenLineCap.Round;
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round;

                        break;
 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID:
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round; 

                        break; 

                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
                        squiggle = true;
                        break; 
                }
 
                double halfLineHeight = lineHeight / 2; 

                // Draw the each of the composition line 
                for (int j = 0; j < attributeRange.CompositionLines.Count; j++)
                {
                    CompositionLine compositionLine = (CompositionLine)attributeRange.CompositionLines[j];
 
                    // Get the start/end point for composition adorner.
                    // Currently Text doesn't aware of the spaceroom for the drawing of the composition 
                    // adorner(like as normal/bold dot/line/squggle), so we should draw the composition adorners 
                    // to the closest area of the bottom text.
                    Point startPoint = new Point(compositionLine.Start.X + clauseGap, compositionLine.Start.Y - halfLineHeight); 
                    Point endPoint = new Point(compositionLine.End.X - clauseGap, compositionLine.End.Y - halfLineHeight);

                    // Apply composition line color which is actually the foreground of text as well
                    pen.Brush = new SolidColorBrush(compositionLine.LineColor); 

                    // Apply matrix to start/end point 
                    // 
                    transform.TryTransform(startPoint, out startPoint);
                    transform.TryTransform(endPoint, out endPoint); 

                    if (squiggle)
                    {
                        // Draw the squiggle line with using of the PathFigure and DrawGemetry. 
                        // We may revisit this logic to render the smooth squiggle line.
                        Point pathPoint = new Point(startPoint.X, startPoint.Y - halfLineHeight); 
 
                        double squiggleGap = halfLineHeight;
 
                        PathFigure pathFigure = new PathFigure();
                        pathFigure.StartPoint = pathPoint;

                        int indexPoint = 0; 

                        while (indexPoint < ((endPoint.X - startPoint.X) / (squiggleGap))) 
                        { 
                            if (indexPoint % 4 == 0 || indexPoint % 4 == 3)
                            { 
                                pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y + halfLineHeight);
                                pathFigure.Segments.Add(new LineSegment(pathPoint, true));
                            }
                            else if (indexPoint % 4 == 1 || indexPoint % 4 == 2) 
                            {
                                pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y - halfLineHeight); 
                                pathFigure.Segments.Add(new LineSegment(pathPoint, true)); 
                            }
 
                            indexPoint++;
                        }

                        PathGeometry pathGeometry = new PathGeometry(); 
                        pathGeometry.Figures.Add(pathFigure);
 
                        // Draw the composition line with the squiggle 
                        drawingContext.DrawGeometry(null, pen, pathGeometry);
                    } 
                    else
                    {
                        drawingContext.DrawLine(pen, startPoint, endPoint);
                    } 
                }
            } 
        } 

        #endregion Protected Events 

        //-----------------------------------------------------
        //
        //  Internal Methods 
        //
        //------------------------------------------------------ 
 
        #region Internal Methods
 
        /// 
        /// Add the composition attribute range that will be rendered.
        /// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
        ///  
        internal void AddAttributeRange(ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServiceDisplayAttribute)
        { 
            // Set the range start/end point's logical direction 
            ITextPointer rangeStart = start.CreatePointer(LogicalDirection.Forward);
            ITextPointer rangeEnd = end.CreatePointer(LogicalDirection.Backward); 

            // Add the composition attribute range
            _attributeRanges.Add(new AttributeRange(_textView, rangeStart, rangeEnd, textServiceDisplayAttribute));
        } 

        ///  
        /// Invalidates the CompositionAdorner render. 
        /// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
        ///  
        internal void InvalidateAdorner()
        {
            for (int i = 0; i < _attributeRanges.Count; i++)
            { 
                // Get the composition attribute range from the attribute range lists
                AttributeRange attributeRange = (AttributeRange)_attributeRanges[i]; 
 
                // Add the composition lines for rendering the composition lines
                attributeRange.AddCompositionLines(); 
            }

            // Invalidate the CompositionAdorner to update the rendering.
            AdornerLayer adornerLayer = VisualTreeHelper.GetParent(this) as AdornerLayer; 
            if (adornerLayer != null)
            { 
                adornerLayer.Update(AdornedElement); 
                adornerLayer.InvalidateArrange();
            } 
        }

        /// 
        /// Add CompositionAdorner to the scoping AdornerLayer. 
        /// 
        internal void Initialize(ITextView textView) 
        { 
            Debug.Assert(_adornerLayer == null, "Attempt to overwrite existing AdornerLayer!");
 
            _adornerLayer = AdornerLayer.GetAdornerLayer(textView.RenderScope);

            if (_adornerLayer != null)
            { 
                // Add the CompositionAdorner to the scoping of AdornerLayer
                _adornerLayer.Add(this); 
            } 
        }
 
        /// 
        /// Remove this CompositionAdorner from its AdornerLayer.
        /// 
        internal void Uninitialize() 
        {
            if (_adornerLayer != null) 
            { 
                // Remove CompositionAdorner form the socping of AdornerLayer
                _adornerLayer.Remove(this); 
                _adornerLayer = null;
            }
        }
 
        #endregion Internal methods
 
        //----------------------------------------------------- 
        //
        //  Private Fields 
        //
        //-----------------------------------------------------

        #region Private Fields 

        // AdornerLayer holding this CompositionAdorner visual 
        private AdornerLayer _adornerLayer; 

        // TextView to which this CompositionAdorner is attached as adorner 
        private ITextView _textView;

        // ArrayList for the composition attribute ranges
        private readonly ArrayList _attributeRanges; 

        // Composition line's dot length 
        private const double DotLength = 1.2; 

        // Ratio of the composition line 
        private const double NormalLineHeightRatio = 0.06;

        // Ratio of the bold composition line
        private const double BoldLineHeightRatio = 0.08; 

        // Ratio of the composition line of dot 
        private const double NormalDotLineHeightRatio = 0.08; 

        // Ratio of the bold composition line of dot 
        private const double BoldDotLineHeightRatio = 0.10;

        // Ratio of the composition line's dash
        private const double NormalDashRatio = 0.27; 

        // Ratio of the bold composition line's dash 
        private const double BoldDashRatio = 0.39; 

        // Ratio of the composition line's clause gap 
        private const double ClauseGapRatio = 0.09;

        // Ratio of the composition line's dash gap
        private const double NormalDashGapRatio = 0.04; 

        // Ratio of the bold composition line's dash gap 
        private const double BoldDashGapRatio = 0.06; 

        #endregion Private Fields 

        //-----------------------------------------------------
        //
        //  Private Class 
        //
        //------------------------------------------------------ 
 
        private class AttributeRange
        { 
            //-----------------------------------------------------
            //
            //  Constructors
            // 
            //------------------------------------------------------
 
            #region Constructors 

            internal AttributeRange(ITextView textView, ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServicesDisplayAttribute) 
            {
                _textView = textView;
                _startOffset = start.Offset;
                _endOffset = end.Offset; 

                _textServicesDisplayAttribute = textServicesDisplayAttribute; 
 
                _compositionLines = new ArrayList(1);
            } 

            #endregion Constructors

            //------------------------------------------------------ 
            //
            //  Internal Methods 
            // 
            //-----------------------------------------------------
 
            #region Internal Methods

            // Add the composition lines for rendering the composition lines.
            internal void AddCompositionLines() 
            {
                // Erase any current lines. 
                _compositionLines.Clear(); 

                ITextPointer start = _textView.TextContainer.Start.CreatePointer(_startOffset, LogicalDirection.Forward); 
                ITextPointer end = _textView.TextContainer.Start.CreatePointer(_endOffset, LogicalDirection.Backward);

                while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
                { 
                    start.MoveToNextContextPosition(LogicalDirection.Forward);
                } 
 
                Invariant.Assert(start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text);
 
                if (end.HasValidLayout)
                {
                    // Get the rectangle for start/end position
                    _startRect = _textView.GetRectangleFromTextPosition(start); 
                    _endRect = _textView.GetRectangleFromTextPosition(end);
 
                    // Check whether the composition line is single or multiple lines 
                    if (_startRect.Top != _endRect.Top)
                    { 
                        // Add the composition lines to be rendered for the composition string
                        AddMultipleCompositionLines(start, end);
                    }
                    else 
                    {
                        // Set the start/end pointer to draw the line 
                        Point startPoint = new Point(_startRect.Left, _startRect.Bottom); 
                        Point endPoint = new Point(_endRect.Right, _endRect.Bottom);
                        Color lineColor = _textServicesDisplayAttribute.GetLineColor(start); 
                        // Add the composition line to be rendered
                        _compositionLines.Add(new CompositionLine(startPoint, endPoint, lineColor));
                    }
                } 
            }
 
            #endregion Internal Methods 

            //------------------------------------------------------ 
            //
            //  Internal Properties
            //
            //----------------------------------------------------- 

            #region Internal Properties 
 
            /// 
            /// Height of the composition attribute range. 
            /// 
            internal double Height
            {
                get 
                {
                    return _startRect.Bottom - _startRect.Top; 
                } 
            }
 
            /// 
            /// CompositionLines of the composition attribute range.
            /// 
            internal ArrayList CompositionLines 
            {
                get 
                { 
                    return _compositionLines;
                } 
            }

            /// 
            /// Composition attribute information. 
            /// 
            internal TextServicesDisplayAttribute TextServicesDisplayAttribute 
            { 
                get
                { 
                    return _textServicesDisplayAttribute;
                }
            }
 
            #endregion Internal Properties
 
            //----------------------------------------------------- 
            //
            //  Private Methods 
            //
            //-----------------------------------------------------

            #region Private Methods 

            private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end) 
            { 
                // Initalize the start/end line pointer
                ITextPointer startLinePointer = start; 
                ITextPointer endLinePointer = startLinePointer;

                // Get all composition lines that includes the start/end pointer
                while (endLinePointer.CompareTo(end) < 0) 
                {
                    TextSegment textSegment = _textView.GetLineRange(endLinePointer); 
 
                    if (textSegment.IsNull)
                    { 
                        // endLinePointer is not within the TextView's definition of a line.
                        // Skip ahead to text on the next iteration.
                        startLinePointer = endLinePointer;
                    } 
                    else
                    { 
                        Debug.Assert(start.CompareTo(startLinePointer) <= 0, "The start pointer is positioned after the composition start line pointer!"); 

                        if (startLinePointer.CompareTo(textSegment.Start) < 0) 
                        {
                            // Update the start line pointer
                            startLinePointer = textSegment.Start;
                        } 

                        if (endLinePointer.CompareTo(textSegment.End) < 0) 
                        { 
                            if (end.CompareTo(textSegment.End) < 0)
                            { 
                                // Update the end line pointer
                                endLinePointer = end.CreatePointer();
                            }
                            else 
                            {
                                // Update the end line pointer 
                                endLinePointer = textSegment.End.CreatePointer(LogicalDirection.Backward); 
                            }
                        } 
                        else
                        {
                            Debug.Assert(endLinePointer.CompareTo(textSegment.End) == 0, "The end line pointer is positioned after the composition text range end pointer!");
                        } 

                        // Get the rectangle for start/end position 
                        Rect startRect = _textView.GetRectangleFromTextPosition(startLinePointer); 
                        Rect endRect = _textView.GetRectangleFromTextPosition(endLinePointer);
 
                        // Set the start/end pointer to draw the line
                        Point startPoint = new Point(startRect.Left, startRect.Bottom);
                        Point endPoint = new Point(endRect.Right, endRect.Bottom);
 
                        // Add the composition line to be rendered
                        _compositionLines.Add(new CompositionLine(startPoint, endPoint, _textServicesDisplayAttribute.GetLineColor(startLinePointer))); 
 
                        startLinePointer = textSegment.End.CreatePointer(LogicalDirection.Forward);
                    } 

                    // Move the start pointer to the next text line. startLinePointer must be a pointer to start
                    // text.
                    while ((startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) && 
                           (startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text))
                    { 
                        startLinePointer.MoveToNextContextPosition(LogicalDirection.Forward); 
                    }
                    endLinePointer = startLinePointer; 
                }
            }

            #endregion Private methods 

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

            #region Private Fields
 
            // TextView to which this CompositionAdorner is attached as adorner
            private ITextView _textView; 
 
            // Start rect of the composition attribute range
            private Rect _startRect; 

            // End rect of the composition attribute range
            private Rect _endRect;
 
            // Start position offset of the composition attribute range
            private readonly int _startOffset; 
 
            // End position offset of the composition attribute range
            private readonly int _endOffset; 

            // Composition display attribute that is specified from IME
            private readonly TextServicesDisplayAttribute _textServicesDisplayAttribute;
 
            // ArrayList for the composition lines
            private readonly ArrayList _compositionLines; 
 
            #endregion Private Fields
        } 

        private class CompositionLine
        {
            //------------------------------------------------------ 
            //
            //  Constructors 
            // 
            //------------------------------------------------------
 
            #region Constructors

            internal CompositionLine(Point start, Point end, Color lineColor)
            { 
                _start = start;
                _end = end; 
                _color = lineColor; 
            }
 
            #endregion Constructors

            //-----------------------------------------------------
            // 
            //  Internal Properties
            // 
            //------------------------------------------------------ 

            #region Internal Properties 

            /// 
            /// Start point of the composition line draw
            ///  
            internal Point Start
            { 
                get 
                {
                    return _start; 
                }
            }

            ///  
            /// End point of the composition line draw
            ///  
            internal Point End 
            {
                get 
                {
                    return _end;
                }
            } 

            ///  
            /// Color of the composition line draw 
            /// 
            internal Color LineColor 
            {
                get
                {
                    return _color; 
                }
            } 
 
            #endregion Internal Properties
 
            //-----------------------------------------------------
            //
            //  Private Fields
            // 
            //-----------------------------------------------------
 
            #region Private Fields 

            // Start point of the composition line draw 
            private Point _start;

            // End point of the composition line draw
            private Point _end; 

            // Color of the composition line draw 
            private Color _color; 

            #endregion Private Fields 
        }
    }
}
 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// File: CompositionAdorner.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Composition adorner to render the composition display attribute. 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Documents
{
    using System.Collections; // ArrayList
    using System.Diagnostics; 
    using System.Windows.Media; // Brush, Transform
    using System.Windows.Controls; // TextBox 
    using System.Windows.Threading; // Dispatcher 
    using MS.Win32;             // TextServices
    using MS.Internal; // Invariant 

    internal class CompositionAdorner : Adorner
    {
        //----------------------------------------------------- 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
 
        #region Constructors

        static CompositionAdorner()
        { 
            // Provide a new default value for the composition adorner so that it is not hit-testable.
            IsEnabledProperty.OverrideMetadata(typeof(CompositionAdorner), new FrameworkPropertyMetadata(false)); 
        } 

        ///  
        /// Creates new instance of CompositionAdorner.
        /// 
        /// 
        /// TextView to which this CompositionAdorner is attached as adorner. 
        /// 
        internal CompositionAdorner(ITextView textView) : this(textView, new ArrayList()) 
        { 
        }
 
        /// 
        /// Creates new instance of CompositionAdorner.
        /// 
        ///  
        /// TextView to which this CompositionAdorner is attached as adorner.
        ///  
        ///  
        /// Attribute ranges
        ///  
        internal CompositionAdorner(ITextView textView, ArrayList attributeRanges)
            : base(textView.RenderScope)
        {
            Debug.Assert(textView != null && textView.RenderScope != null); 

            // TextView to which this CompositionAdorner is attached as adorner and it will 
            // als be used for GetRectangleFromTextPosition/GetLineRange 
            _textView = textView;
 
            // Create ArrayList for the composition attribute ranges and composition lines
            _attributeRanges = attributeRanges;
        }
 
        #endregion Constructors
 
        //------------------------------------------------------ 
        //
        //  Public Methods 
        //
        //-----------------------------------------------------

        #region Public Methods 

        ///  
        /// Add a transform so that the composition adorner gets positioned at the correct spot within the text being edited 
        /// 
        ///  
        /// The transform applied to the object the adorner adorns
        /// 
        /// 
        /// Transform to apply to the adorner 
        /// 
        public override GeneralTransform GetDesiredTransform(GeneralTransform transform) 
        { 
            TranslateTransform translation;
            GeneralTransformGroup group = new GeneralTransformGroup(); 

            // Get the matrix transform out, skip all non affine transforms
            Transform t = transform.AffineTransform;
            if (t == null) 
            {
                t = Transform.Identity; 
            } 

            // Translate the adorner to (0, 0) point 
            translation = new TranslateTransform(-(t.Value.OffsetX), -(t.Value.OffsetY));

            group.Children.Add(translation);
 
            if (transform != null)
            { 
                group.Children.Add(transform); 
            }
 
            return group;
        }

        #endregion Public Methods 

        //------------------------------------------------------ 
        // 
        //  Protected Methods
        // 
        //------------------------------------------------------

        #region Protected Methods
 
        /// 
        /// Render override to render the composition adorner here. 
        ///  
        protected override void OnRender(DrawingContext drawingContext)
        { 
            // Get the matrix from AdornedElement to the visual parent to get the transformed
            // start/end point
            Visual parent2d = VisualTreeHelper.GetParent(this.AdornedElement) as Visual;
            if (parent2d == null) 
            {
                return; 
            } 

            GeneralTransform transform = AdornedElement.TransformToAncestor(parent2d); 
            if (transform == null)
            {
                return;
            } 

            // Render the each of the composition string attribute from the attribute ranges. 
            for (int i = 0; i < _attributeRanges.Count; i++) 
            {
                DoubleCollection dashArray; 

                // Get the composition attribute range from the attribute range lists
                AttributeRange attributeRange = (AttributeRange)_attributeRanges[i];
 
                // Skip the rendering composition lines if the composition line doesn't exist.
                if (attributeRange.CompositionLines.Count == 0) 
                { 
                    continue;
                } 

                // Set the line bold and squiggle
                bool lineBold = attributeRange.TextServicesDisplayAttribute.IsBoldLine ? true : false;
                bool squiggle = false; 

                // Set the line height and cluse gap value that base on the ratio of text height 
                double height = attributeRange.Height; 
                double lineHeight = height * (lineBold ? BoldLineHeightRatio : NormalLineHeightRatio);
                double clauseGap = height * ClauseGapRatio; 

                // Create Pen for drawing the composition lines with the specified line color
                Pen pen = new Pen(new SolidColorBrush(Colors.Black), lineHeight);
 
                // Set the pen style that based on IME's composition line style
                switch (attributeRange.TextServicesDisplayAttribute.LineStyle) 
                { 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT:
                        // Add the dot length and specify the start/end line cap as the round 
                        dashArray = new DoubleCollection();
                        dashArray.Add(DotLength);
                        dashArray.Add(DotLength);
 
                        pen.DashStyle = new DashStyle(dashArray, 0);
                        pen.DashCap = System.Windows.Media.PenLineCap.Round; 
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round;
 
                        // Update the line height for the dot line. Dot line will be more thickness than
                        // other line to show it clearly.
                        lineHeight = height * (lineBold ? BoldDotLineHeightRatio : NormalDotLineHeightRatio);
 
                        break;
 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DASH: 
                        double dashLength = height * (lineBold ? BoldDashRatio : NormalDashRatio);
                        double dashGapLength = height * (lineBold ? BoldDashGapRatio : NormalDashGapRatio); 

                        // Add the dash and dash gap legth
                        dashArray = new DoubleCollection();
                        dashArray.Add(dashLength); 
                        dashArray.Add(dashGapLength);
 
                        pen.DashStyle = new DashStyle(dashArray, 0); 
                        pen.DashCap = System.Windows.Media.PenLineCap.Round;
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round;

                        break;
 
                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID:
                        pen.StartLineCap = System.Windows.Media.PenLineCap.Round; 
                        pen.EndLineCap = System.Windows.Media.PenLineCap.Round; 

                        break; 

                    case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
                        squiggle = true;
                        break; 
                }
 
                double halfLineHeight = lineHeight / 2; 

                // Draw the each of the composition line 
                for (int j = 0; j < attributeRange.CompositionLines.Count; j++)
                {
                    CompositionLine compositionLine = (CompositionLine)attributeRange.CompositionLines[j];
 
                    // Get the start/end point for composition adorner.
                    // Currently Text doesn't aware of the spaceroom for the drawing of the composition 
                    // adorner(like as normal/bold dot/line/squggle), so we should draw the composition adorners 
                    // to the closest area of the bottom text.
                    Point startPoint = new Point(compositionLine.Start.X + clauseGap, compositionLine.Start.Y - halfLineHeight); 
                    Point endPoint = new Point(compositionLine.End.X - clauseGap, compositionLine.End.Y - halfLineHeight);

                    // Apply composition line color which is actually the foreground of text as well
                    pen.Brush = new SolidColorBrush(compositionLine.LineColor); 

                    // Apply matrix to start/end point 
                    // 
                    transform.TryTransform(startPoint, out startPoint);
                    transform.TryTransform(endPoint, out endPoint); 

                    if (squiggle)
                    {
                        // Draw the squiggle line with using of the PathFigure and DrawGemetry. 
                        // We may revisit this logic to render the smooth squiggle line.
                        Point pathPoint = new Point(startPoint.X, startPoint.Y - halfLineHeight); 
 
                        double squiggleGap = halfLineHeight;
 
                        PathFigure pathFigure = new PathFigure();
                        pathFigure.StartPoint = pathPoint;

                        int indexPoint = 0; 

                        while (indexPoint < ((endPoint.X - startPoint.X) / (squiggleGap))) 
                        { 
                            if (indexPoint % 4 == 0 || indexPoint % 4 == 3)
                            { 
                                pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y + halfLineHeight);
                                pathFigure.Segments.Add(new LineSegment(pathPoint, true));
                            }
                            else if (indexPoint % 4 == 1 || indexPoint % 4 == 2) 
                            {
                                pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y - halfLineHeight); 
                                pathFigure.Segments.Add(new LineSegment(pathPoint, true)); 
                            }
 
                            indexPoint++;
                        }

                        PathGeometry pathGeometry = new PathGeometry(); 
                        pathGeometry.Figures.Add(pathFigure);
 
                        // Draw the composition line with the squiggle 
                        drawingContext.DrawGeometry(null, pen, pathGeometry);
                    } 
                    else
                    {
                        drawingContext.DrawLine(pen, startPoint, endPoint);
                    } 
                }
            } 
        } 

        #endregion Protected Events 

        //-----------------------------------------------------
        //
        //  Internal Methods 
        //
        //------------------------------------------------------ 
 
        #region Internal Methods
 
        /// 
        /// Add the composition attribute range that will be rendered.
        /// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
        ///  
        internal void AddAttributeRange(ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServiceDisplayAttribute)
        { 
            // Set the range start/end point's logical direction 
            ITextPointer rangeStart = start.CreatePointer(LogicalDirection.Forward);
            ITextPointer rangeEnd = end.CreatePointer(LogicalDirection.Backward); 

            // Add the composition attribute range
            _attributeRanges.Add(new AttributeRange(_textView, rangeStart, rangeEnd, textServiceDisplayAttribute));
        } 

        ///  
        /// Invalidates the CompositionAdorner render. 
        /// Used in TextServicesDisplayAttributePropertyRanges (for composition display attribute)
        ///  
        internal void InvalidateAdorner()
        {
            for (int i = 0; i < _attributeRanges.Count; i++)
            { 
                // Get the composition attribute range from the attribute range lists
                AttributeRange attributeRange = (AttributeRange)_attributeRanges[i]; 
 
                // Add the composition lines for rendering the composition lines
                attributeRange.AddCompositionLines(); 
            }

            // Invalidate the CompositionAdorner to update the rendering.
            AdornerLayer adornerLayer = VisualTreeHelper.GetParent(this) as AdornerLayer; 
            if (adornerLayer != null)
            { 
                adornerLayer.Update(AdornedElement); 
                adornerLayer.InvalidateArrange();
            } 
        }

        /// 
        /// Add CompositionAdorner to the scoping AdornerLayer. 
        /// 
        internal void Initialize(ITextView textView) 
        { 
            Debug.Assert(_adornerLayer == null, "Attempt to overwrite existing AdornerLayer!");
 
            _adornerLayer = AdornerLayer.GetAdornerLayer(textView.RenderScope);

            if (_adornerLayer != null)
            { 
                // Add the CompositionAdorner to the scoping of AdornerLayer
                _adornerLayer.Add(this); 
            } 
        }
 
        /// 
        /// Remove this CompositionAdorner from its AdornerLayer.
        /// 
        internal void Uninitialize() 
        {
            if (_adornerLayer != null) 
            { 
                // Remove CompositionAdorner form the socping of AdornerLayer
                _adornerLayer.Remove(this); 
                _adornerLayer = null;
            }
        }
 
        #endregion Internal methods
 
        //----------------------------------------------------- 
        //
        //  Private Fields 
        //
        //-----------------------------------------------------

        #region Private Fields 

        // AdornerLayer holding this CompositionAdorner visual 
        private AdornerLayer _adornerLayer; 

        // TextView to which this CompositionAdorner is attached as adorner 
        private ITextView _textView;

        // ArrayList for the composition attribute ranges
        private readonly ArrayList _attributeRanges; 

        // Composition line's dot length 
        private const double DotLength = 1.2; 

        // Ratio of the composition line 
        private const double NormalLineHeightRatio = 0.06;

        // Ratio of the bold composition line
        private const double BoldLineHeightRatio = 0.08; 

        // Ratio of the composition line of dot 
        private const double NormalDotLineHeightRatio = 0.08; 

        // Ratio of the bold composition line of dot 
        private const double BoldDotLineHeightRatio = 0.10;

        // Ratio of the composition line's dash
        private const double NormalDashRatio = 0.27; 

        // Ratio of the bold composition line's dash 
        private const double BoldDashRatio = 0.39; 

        // Ratio of the composition line's clause gap 
        private const double ClauseGapRatio = 0.09;

        // Ratio of the composition line's dash gap
        private const double NormalDashGapRatio = 0.04; 

        // Ratio of the bold composition line's dash gap 
        private const double BoldDashGapRatio = 0.06; 

        #endregion Private Fields 

        //-----------------------------------------------------
        //
        //  Private Class 
        //
        //------------------------------------------------------ 
 
        private class AttributeRange
        { 
            //-----------------------------------------------------
            //
            //  Constructors
            // 
            //------------------------------------------------------
 
            #region Constructors 

            internal AttributeRange(ITextView textView, ITextPointer start, ITextPointer end, TextServicesDisplayAttribute textServicesDisplayAttribute) 
            {
                _textView = textView;
                _startOffset = start.Offset;
                _endOffset = end.Offset; 

                _textServicesDisplayAttribute = textServicesDisplayAttribute; 
 
                _compositionLines = new ArrayList(1);
            } 

            #endregion Constructors

            //------------------------------------------------------ 
            //
            //  Internal Methods 
            // 
            //-----------------------------------------------------
 
            #region Internal Methods

            // Add the composition lines for rendering the composition lines.
            internal void AddCompositionLines() 
            {
                // Erase any current lines. 
                _compositionLines.Clear(); 

                ITextPointer start = _textView.TextContainer.Start.CreatePointer(_startOffset, LogicalDirection.Forward); 
                ITextPointer end = _textView.TextContainer.Start.CreatePointer(_endOffset, LogicalDirection.Backward);

                while (start.CompareTo(end) < 0 && start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
                { 
                    start.MoveToNextContextPosition(LogicalDirection.Forward);
                } 
 
                Invariant.Assert(start.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text);
 
                if (end.HasValidLayout)
                {
                    // Get the rectangle for start/end position
                    _startRect = _textView.GetRectangleFromTextPosition(start); 
                    _endRect = _textView.GetRectangleFromTextPosition(end);
 
                    // Check whether the composition line is single or multiple lines 
                    if (_startRect.Top != _endRect.Top)
                    { 
                        // Add the composition lines to be rendered for the composition string
                        AddMultipleCompositionLines(start, end);
                    }
                    else 
                    {
                        // Set the start/end pointer to draw the line 
                        Point startPoint = new Point(_startRect.Left, _startRect.Bottom); 
                        Point endPoint = new Point(_endRect.Right, _endRect.Bottom);
                        Color lineColor = _textServicesDisplayAttribute.GetLineColor(start); 
                        // Add the composition line to be rendered
                        _compositionLines.Add(new CompositionLine(startPoint, endPoint, lineColor));
                    }
                } 
            }
 
            #endregion Internal Methods 

            //------------------------------------------------------ 
            //
            //  Internal Properties
            //
            //----------------------------------------------------- 

            #region Internal Properties 
 
            /// 
            /// Height of the composition attribute range. 
            /// 
            internal double Height
            {
                get 
                {
                    return _startRect.Bottom - _startRect.Top; 
                } 
            }
 
            /// 
            /// CompositionLines of the composition attribute range.
            /// 
            internal ArrayList CompositionLines 
            {
                get 
                { 
                    return _compositionLines;
                } 
            }

            /// 
            /// Composition attribute information. 
            /// 
            internal TextServicesDisplayAttribute TextServicesDisplayAttribute 
            { 
                get
                { 
                    return _textServicesDisplayAttribute;
                }
            }
 
            #endregion Internal Properties
 
            //----------------------------------------------------- 
            //
            //  Private Methods 
            //
            //-----------------------------------------------------

            #region Private Methods 

            private void AddMultipleCompositionLines(ITextPointer start, ITextPointer end) 
            { 
                // Initalize the start/end line pointer
                ITextPointer startLinePointer = start; 
                ITextPointer endLinePointer = startLinePointer;

                // Get all composition lines that includes the start/end pointer
                while (endLinePointer.CompareTo(end) < 0) 
                {
                    TextSegment textSegment = _textView.GetLineRange(endLinePointer); 
 
                    if (textSegment.IsNull)
                    { 
                        // endLinePointer is not within the TextView's definition of a line.
                        // Skip ahead to text on the next iteration.
                        startLinePointer = endLinePointer;
                    } 
                    else
                    { 
                        Debug.Assert(start.CompareTo(startLinePointer) <= 0, "The start pointer is positioned after the composition start line pointer!"); 

                        if (startLinePointer.CompareTo(textSegment.Start) < 0) 
                        {
                            // Update the start line pointer
                            startLinePointer = textSegment.Start;
                        } 

                        if (endLinePointer.CompareTo(textSegment.End) < 0) 
                        { 
                            if (end.CompareTo(textSegment.End) < 0)
                            { 
                                // Update the end line pointer
                                endLinePointer = end.CreatePointer();
                            }
                            else 
                            {
                                // Update the end line pointer 
                                endLinePointer = textSegment.End.CreatePointer(LogicalDirection.Backward); 
                            }
                        } 
                        else
                        {
                            Debug.Assert(endLinePointer.CompareTo(textSegment.End) == 0, "The end line pointer is positioned after the composition text range end pointer!");
                        } 

                        // Get the rectangle for start/end position 
                        Rect startRect = _textView.GetRectangleFromTextPosition(startLinePointer); 
                        Rect endRect = _textView.GetRectangleFromTextPosition(endLinePointer);
 
                        // Set the start/end pointer to draw the line
                        Point startPoint = new Point(startRect.Left, startRect.Bottom);
                        Point endPoint = new Point(endRect.Right, endRect.Bottom);
 
                        // Add the composition line to be rendered
                        _compositionLines.Add(new CompositionLine(startPoint, endPoint, _textServicesDisplayAttribute.GetLineColor(startLinePointer))); 
 
                        startLinePointer = textSegment.End.CreatePointer(LogicalDirection.Forward);
                    } 

                    // Move the start pointer to the next text line. startLinePointer must be a pointer to start
                    // text.
                    while ((startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) && 
                           (startLinePointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text))
                    { 
                        startLinePointer.MoveToNextContextPosition(LogicalDirection.Forward); 
                    }
                    endLinePointer = startLinePointer; 
                }
            }

            #endregion Private methods 

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

            #region Private Fields
 
            // TextView to which this CompositionAdorner is attached as adorner
            private ITextView _textView; 
 
            // Start rect of the composition attribute range
            private Rect _startRect; 

            // End rect of the composition attribute range
            private Rect _endRect;
 
            // Start position offset of the composition attribute range
            private readonly int _startOffset; 
 
            // End position offset of the composition attribute range
            private readonly int _endOffset; 

            // Composition display attribute that is specified from IME
            private readonly TextServicesDisplayAttribute _textServicesDisplayAttribute;
 
            // ArrayList for the composition lines
            private readonly ArrayList _compositionLines; 
 
            #endregion Private Fields
        } 

        private class CompositionLine
        {
            //------------------------------------------------------ 
            //
            //  Constructors 
            // 
            //------------------------------------------------------
 
            #region Constructors

            internal CompositionLine(Point start, Point end, Color lineColor)
            { 
                _start = start;
                _end = end; 
                _color = lineColor; 
            }
 
            #endregion Constructors

            //-----------------------------------------------------
            // 
            //  Internal Properties
            // 
            //------------------------------------------------------ 

            #region Internal Properties 

            /// 
            /// Start point of the composition line draw
            ///  
            internal Point Start
            { 
                get 
                {
                    return _start; 
                }
            }

            ///  
            /// End point of the composition line draw
            ///  
            internal Point End 
            {
                get 
                {
                    return _end;
                }
            } 

            ///  
            /// Color of the composition line draw 
            /// 
            internal Color LineColor 
            {
                get
                {
                    return _color; 
                }
            } 
 
            #endregion Internal Properties
 
            //-----------------------------------------------------
            //
            //  Private Fields
            // 
            //-----------------------------------------------------
 
            #region Private Fields 

            // Start point of the composition line draw 
            private Point _start;

            // End point of the composition line draw
            private Point _end; 

            // Color of the composition line draw 
            private Color _color; 

            #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