TextSelection.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Documents / TextSelection.cs / 4 / TextSelection.cs

                            //---------------------------------------------------------------------------- 
//
// File: TextSelection.cs
//
// Copyright (C) Microsoft Corporation.  All rights reserved. 
//
// Description: Holds and manipulates the text selection state for TextEditor. 
// 
//---------------------------------------------------------------------------
 
namespace System.Windows.Documents
{
    using MS.Internal;
    using System.Collections.Generic; 
    using System.Globalization;
    using System.Windows.Controls.Primitives;  // TextBoxBase 
    using System.Windows.Input; 
    using System.Windows.Media;
    using System.Windows.Threading; 
    using System.Threading;
    using System.Security;
    using System.Security.Permissions;
    using System.IO; 
    using System.Xml;
    using MS.Win32; 
 
    /// 
    /// The TextSelection class encapsulates selection state for the RichTextBox 
    /// control.  It has no public constructor, but is exposed via a public
    /// property on RichTextBox.
    /// 
    public sealed class TextSelection : TextRange, ITextSelection 
    {
        #region Constructors 
 
        //-----------------------------------------------------
        // 
        //  Constructors
        //
        //-----------------------------------------------------
 
        // Contstructor.
        // TextSelection does not have a public constructor.  It is only accessible 
        // through TextEditor's Selection property. 
        internal TextSelection(TextEditor textEditor)
            : base(textEditor.TextContainer.Start, textEditor.TextContainer.Start) 
        {
            ITextSelection thisSelection = (ITextSelection)this;

            Invariant.Assert(textEditor.UiScope != null); 

            // Attach the selection to its editor 
            _textEditor = textEditor; 

            // Initialize active pointers of the selection - anchor and moving pointers 
            SetActivePositions(/*AnchorPosition:*/thisSelection.Start, thisSelection.End);

            // Activate selection in case if this control has keyboard focus already
            thisSelection.UpdateCaretAndHighlight(); 
        }
 
        #endregion Constructors 

        // ****************************************************** 
        // *****************************************************
        // ******************************************************
        //
        // Abstract TextSelection Implementation 
        //
        // ****************************************************** 
        // ***************************************************** 
        // ******************************************************
 
        //-----------------------------------------------------
        //
        // ITextRange implementation
        // 
        //-----------------------------------------------------
 
        #region ITextRange Implementation 

        //...................................................... 
        //
        // Selection Building
        //
        //...................................................... 

        ///  
        ///  
        /// 
        void ITextRange.Select(ITextPointer anchorPosition, ITextPointer movingPosition) 
        {
            TextRangeBase.BeginChange(this);
            try
            { 
                TextRangeBase.Select(this, anchorPosition, movingPosition);
                SetActivePositions(anchorPosition, movingPosition); 
            } 
            finally
            { 
                TextRangeBase.EndChange(this);
            }
        }
 
        /// 
        ///  
        ///  
        void ITextRange.SelectWord(ITextPointer position)
        { 
            TextRangeBase.BeginChange(this);
            try
            {
                TextRangeBase.SelectWord(this, position); 

                ITextSelection thisSelection = this; 
                SetActivePositions(/*anchorPosition:*/thisSelection.Start, thisSelection.End); 
            }
            finally 
            {
                TextRangeBase.EndChange(this);
            }
        } 

        ///  
        ///  
        /// 
        void ITextRange.SelectParagraph(ITextPointer position) 
        {
            TextRangeBase.BeginChange(this);
            try
            { 
                TextRangeBase.SelectParagraph(this, position);
 
                // 
                ITextSelection thisSelection = this;
                SetActivePositions(/*anchorPosition:*/position, thisSelection.End); 
            }
            finally
            {
                TextRangeBase.EndChange(this); 
            }
        } 
 
        /// 
        ///  
        /// 
        void ITextRange.ApplyTypingHeuristics(bool overType)
        {
            TextRangeBase.BeginChange(this); 
            try
            { 
                TextRangeBase.ApplyInitialTypingHeuristics(this); 

                // For non-empty selection start with saving current formatting 
                if (!this.IsEmpty && _textEditor.AcceptsRichContent)
                {
                    SpringloadCurrentFormatting();
                    // Note: we springload formatting before overtype expansion 
                }
 
                TextRangeBase.ApplyFinalTypingHeuristics(this, overType); 
            }
            finally 
            {
                TextRangeBase.EndChange(this);
            }
        } 

        ///  
        ///  
        /// 
        object ITextRange.GetPropertyValue(DependencyProperty formattingProperty) 
        {
            object propertyValue;

            if (this.IsEmpty && TextSchema.IsCharacterProperty(formattingProperty)) 
            {
                // For empty selection return springloaded formatting (only character formatting properties can be springloaded) 
                propertyValue = ((TextSelection)this).GetCurrentValue(formattingProperty); 
            }
            else 
            {
                // Otherwise return base implementation from a TextRange.
                propertyValue = TextRangeBase.GetPropertyValue(this, formattingProperty);
            } 

            return propertyValue; 
        } 

        //...................................................... 
        //
        // Plain Text Modification
        //
        //...................................................... 

        //----------------------------------------------------- 
        // 
        //  Overrides
        // 
        //------------------------------------------------------

        // Set true if a Changed event is pending.
        bool ITextRange._IsChanged 
        {
            get 
            { 
                return _IsChanged;
            } 

            set
            {
                // TextStore needs to know about state changes 
                // from false to true.
                if (!_IsChanged && value) 
                { 
                    if (this.TextStore != null)
                    { 
                        this.TextStore.OnSelectionChange();
                    }
                    if (this.ImmComposition != null)
                    { 
                        this.ImmComposition.OnSelectionChange();
                    } 
                } 

                _IsChanged = value; 
            }
        }

        ///  
        /// 
        void ITextRange.NotifyChanged(bool disableScroll, bool skipEvents) 
        { 
            // Notify text store about selection movement.
            if (this.TextStore != null) 
            {
                this.TextStore.OnSelectionChanged();
            }
            // Notify ImmComposition about selection movement. 
            if (this.ImmComposition != null)
            { 
                this.ImmComposition.OnSelectionChanged(); 
            }
 
            if (!skipEvents)
            {
                TextRangeBase.NotifyChanged(this, disableScroll);
            } 

            if (!disableScroll) 
            { 
                // Force a synchronous layout update.  If the update was big enough, background layout
                // kicked in and the caret won't otherwise be updated.  Note this will block the thread 
                // while layout runs.
                //
                // It's possible an application repositioned the caret
                // while listening to a change event just raised, but in that case the following 
                // code should be harmless.
                ITextPointer movingPosition = ((ITextSelection)this).MovingPosition; 
                if (this.TextView != null && this.TextView.IsValid && 
                    !this.TextView.Contains(movingPosition))
                { 
                    movingPosition.ValidateLayout();
                }
                // If layout wasn't valid, then there's a pending caret update
                // that will proceed correctly now.  Otherwise the whole operation 
                // is a nop.
            } 
 
            // Fixup the caret.
            UpdateCaretState(disableScroll ? CaretScrollMethod.None : CaretScrollMethod.Simple); 
        }

        //-----------------------------------------------------
        // 
        //  ITextRange Properties
        // 
        //------------------------------------------------------ 

        //...................................................... 
        //
        //  Content - rich and plain
        //
        //...................................................... 

        ///  
        ///  
        /// 
        string ITextRange.Text 
        {
            get
            {
                return TextRangeBase.GetText(this); 
            }
 
            set 
            {
                TextRangeBase.BeginChange(this); 
                try
                {
                    TextRangeBase.SetText(this, value);
 
                    if (this.IsEmpty)
                    { 
                        // We need to ensure appropriate caret visual position. 
                        ((ITextSelection)this).SetCaretToPosition(((ITextRange)this).End, LogicalDirection.Forward, /*allowStopAtLineEnd:*/false, /*allowStopNearSpace:*/false);
                    } 

                    Invariant.Assert(!this.IsTableCellRange);
                    SetActivePositions(((ITextRange)this).Start, ((ITextRange)this).End);
                } 
                finally
                { 
                    TextRangeBase.EndChange(this); 
                }
            } 
        }

        #endregion ITextRange Implementation
 
        //------------------------------------------------------
        // 
        // ITextSelection implementation 
        //
        //----------------------------------------------------- 

        #region ITextSelection Implementation

        void ITextSelection.UpdateCaretAndHighlight() 
        {
            if (this.UiScope.IsEnabled && this.TextView != null && this.UiScope.IsKeyboardFocused) 
            { 
                // Update the TLS first, so that EnsureCaret is working in
                // the correct context. 
                SetThreadSelection();

                // Make caret visible and blinking
                EnsureCaret(/*isBlinkEnabled:*/true, CaretScrollMethod.None); 
                // Highlight selection
                Highlight(); 
            } 
            else if (this.UiScope.IsEnabled && this.TextView != null &&
             this.UiScope.IsFocused && IsRootElement(FocusManager.GetFocusScope(this.UiScope)) && 
             (IsFocusWithinRoot() ||                   // either UiScope root window has keyboard focus
             _textEditor.IsContextMenuOpen))          // or UiScope has a context menu open
            {
                // Update the TLS first, so that EnsureCaret is working in 
                // the correct context.
                SetThreadSelection(); 
 
                // just stop the caret from blinking
                EnsureCaret(/*isBlinkEnabled:*/false, CaretScrollMethod.None); 
                // Highlight selection
                Highlight();
            }
            else 
            {
                // Update the TLS first, so that the caret is working in 
                // the correct context. 
                ClearThreadSelection();
 
                // delete the caret
                DetachCaretFromVisualTree();
                // Remove highlight
                Unhighlight(); 
            }
        } 
 
        /// 
        ///  
        ITextPointer ITextSelection.AnchorPosition
        {
            get
            { 
                Invariant.Assert(this.IsEmpty || _anchorPosition != null);
                Invariant.Assert(_anchorPosition == null || _anchorPosition.IsFrozen); 
                return this.IsEmpty ? ((ITextSelection)this).Start : _anchorPosition; 
            }
        } 

        /// 
        /// The position within this selection that responds to user input.
        ///  
        ITextPointer ITextSelection.MovingPosition
        { 
            get 
            {
                ITextSelection thisSelection = this; 
                ITextPointer movingPosition;

                if (this.IsEmpty)
                { 
                    movingPosition = thisSelection.Start;
                } 
                else 
                {
                    switch (_movingPositionEdge) 
                    {
                        case MovingEdge.Start:
                            movingPosition = thisSelection.Start;
                            break; 

                        case MovingEdge.StartInner: 
                            movingPosition = thisSelection.TextSegments[0].End; 
                            break;
 
                        case MovingEdge.EndInner:
                            movingPosition = thisSelection.TextSegments[thisSelection.TextSegments.Count - 1].Start;
                            break;
 
                        case MovingEdge.End:
                            movingPosition = thisSelection.End; 
                            break; 

                        case MovingEdge.None: 
                        default:
                            Invariant.Assert(false, "MovingEdge should never be None with non-empty TextSelection!");
                            movingPosition = null;
                            break; 
                    }
 
                    movingPosition = movingPosition.GetFrozenPointer(_movingPositionDirection); 
                }
 
                return movingPosition;
            }
        }
 
        /// 
        ///  
        void ITextSelection.SetCaretToPosition(ITextPointer caretPosition, LogicalDirection direction, bool allowStopAtLineEnd, bool allowStopNearSpace) 
        {
            // We need a pointer with appropriate direction, 
            // becasue it will be used in textRangeBase.Select method for
            // pointer normalization.
            caretPosition = caretPosition.CreatePointer(direction);
 
            // Normalize the position in its logical direction - to get to text content over there.
            caretPosition.MoveToInsertionPosition(direction); 
 
            // We need a pointer with the reverse direction to confirm
            // the line wrapping position. So we can ensure Bidi caret navigation. 
            // Bidi can have the different caret position by setting the
            // logical direction, so we have to only set the logical direction
            // as the forward for the real line wrapping position.
            ITextPointer reversePosition = caretPosition.CreatePointer(direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward); 

            // Check line wrapping condition 
            if (!allowStopAtLineEnd && 
                ((TextPointerBase.IsAtLineWrappingPosition(caretPosition, this.TextView) &&
                  TextPointerBase.IsAtLineWrappingPosition(reversePosition, this.TextView)) || 
                 TextPointerBase.IsNextToPlainLineBreak(caretPosition, LogicalDirection.Backward) ||
                 TextSchema.IsBreak(caretPosition.GetElementType(LogicalDirection.Backward))))
            {
                // Caret is at wrapping position, and we are not allowed to stay at end of line, 
                // so we choose forward direction to appear in the begiinning of a second line
                caretPosition.SetLogicalDirection(LogicalDirection.Forward); 
            } 
            else
            { 
                if (caretPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.Text &&
                    caretPosition.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
                {
                    // This is statistically most typical case. No "smartness" needed 
                    // to choose standard Forward orientation for the caret.
                    // NOTE: By using caretPosition's direction we solve BiDi caret orientation case: 
                    // The orietnation reflects a direction from where caret has been moved 
                    // or orientation where mouse clicked. So we will stick with appropriate
                    // character. 

                    // Nothing to do. The caretPosition is good to go.
                }
                else if (!allowStopNearSpace) 
                {
                    // There are some tags around, and we are not allowed to choose a side near to space. 
                    // So we need to perform some content analysis. 

                    char[] charBuffer = new char[1]; 

                    if (caretPosition.GetPointerContext(direction) == TextPointerContext.Text &&
                        caretPosition.GetTextInRun(direction, charBuffer, 0, 1) == 1 &&
                        Char.IsWhiteSpace(charBuffer[0])) 
                    {
                        LogicalDirection oppositeDirection = direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward; 
 
                        // Check formatting switch condition at this position
                        FlowDirection initialFlowDirection = (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty); 

                        bool moved = caretPosition.MoveToInsertionPosition(oppositeDirection);

                        if (moved && 
                            initialFlowDirection == (FlowDirection)caretPosition.GetValue(FrameworkElement.FlowDirectionProperty) &&
                            (caretPosition.GetPointerContext(oppositeDirection) != TextPointerContext.Text || 
                             caretPosition.GetTextInRun(oppositeDirection, charBuffer, 0, 1) != 1 || 
                             !Char.IsWhiteSpace(charBuffer[0])))
                        { 
                            // In the opposite direction we have a non-space
                            // character. So we choose that direction
                            direction = oppositeDirection;
                            caretPosition.SetLogicalDirection(direction); 
                        }
                    } 
                } 
            }
 
            // Now that orientation of a caretPosition is identified,
            // build an empty selection at this position
            TextRangeBase.BeginChange(this);
            try 
            {
                TextRangeBase.Select(this, caretPosition, caretPosition); 
 
                // Note how Select method works for the case of empty range:
                // It creates a single instance TextPointer normalized and oriented 
                // in a direction taken from caretPosition:
                ITextSelection thisSelection = this;
                Invariant.Assert(thisSelection.Start.LogicalDirection == caretPosition.LogicalDirection); // orientation must be as passed
                Invariant.Assert(this.IsEmpty); 
                //Invariant.Assert((object)thisSelection.Start == (object)thisSelection.End); // it must be the same instance of TextPointer
                //Invariant.Assert(TextPointerBase.IsAtInsertionPosition(thisSelection.Start, caretPosition.LogicalDirection)); // normalization must be done in the same diredction as orientation 
 
                // Clear active positions when selection is empty
                SetActivePositions(null, null); 
            }
            finally
            {
                TextRangeBase.EndChange(this); 
            }
        } 
 
        //......................................................
        // 
        //  Extending via movingEnd movements
        //
        //......................................................
 
        // Worker for ExtendToPosition, handles all ITextContainers.
        void ITextSelection.ExtendToPosition(ITextPointer position) 
        { 
            TextRangeBase.BeginChange(this);
            try 
            {
                ITextSelection thisSelection = (ITextSelection)this;

                // Store initial anchor position 
                ITextPointer anchorPosition = thisSelection.AnchorPosition;
 
                //Build new selection 
                TextRangeBase.Select(thisSelection, anchorPosition, position);
 
                // Store active positions.
                SetActivePositions(anchorPosition, position);
            }
            finally 
            {
                TextRangeBase.EndChange(this); 
            } 
        }
 
        // Worker for ExtendToNextInsertionPosition, handles all ITextContainers.
        bool ITextSelection.ExtendToNextInsertionPosition(LogicalDirection direction)
        {
            bool moved = false; 

            TextRangeBase.BeginChange(this); 
            try 
            {
                ITextPointer anchorPosition = ((ITextSelection)this).AnchorPosition; 
                ITextPointer movingPosition = ((ITextSelection)this).MovingPosition;
                ITextPointer newMovingPosition;

                if (this.IsTableCellRange) 
                {
                    // Both moving and anchor positions are within a single Table, in seperate 
                    // cells.  Select next cell. 
                    newMovingPosition = TextRangeEditTables.GetNextTableCellRangeInsertionPosition(this, direction);
                } 
                else if (movingPosition is TextPointer && TextPointerBase.IsAtRowEnd(movingPosition))
                {
                    // Moving position at at a Table row end, anchor position is outside
                    // the Table.  Select next row. 
                    newMovingPosition = TextRangeEditTables.GetNextRowEndMovingPosition(this, direction);
                } 
                else if (movingPosition is TextPointer && TextRangeEditTables.MovingPositionCrossesCellBoundary(this)) 
                {
                    // Moving position at at a Table row start, anchor position is outside 
                    // the Table.  Select next row.
                    newMovingPosition = TextRangeEditTables.GetNextRowStartMovingPosition(this, direction);
                }
                else 
                {
                    // No Table logic. 
                    newMovingPosition = GetNextTextSegmentInsertionPosition(direction); 
                }
 
                if (newMovingPosition == null && direction == LogicalDirection.Forward)
                {
                    // When moving forward we cannot find next insertion position, set the end of selection after the last paragraph
                    // (which is not an insertion position) 
                    if (movingPosition.CompareTo(movingPosition.TextContainer.End) != 0)
                    { 
                        newMovingPosition = movingPosition.TextContainer.End; 
                    }
                } 

                // Now that new movingPosition is prepared, build the new selection
                if (newMovingPosition != null)
                { 
                    moved = true;
 
                    // Re-build a range for the new pair of positions 
                    TextRangeBase.Select(this, anchorPosition, newMovingPosition);
 
                    // Make sure that active positions are inside of a selection
                    // Set the moving position direction to point toward the inner content.
                    LogicalDirection contentDirection = (anchorPosition.CompareTo(newMovingPosition) <= 0) ?
                                                 LogicalDirection.Backward : LogicalDirection.Forward; 
                    newMovingPosition = newMovingPosition.GetFrozenPointer(contentDirection);
 
                    SetActivePositions(anchorPosition, newMovingPosition); 
                }
            } 
            finally
            {
                TextRangeBase.EndChange(this);
            } 

            return moved; 
        } 

        // Finds new movingPosition for the selection when it is in TextSegment state. 
        // Returns null if there is no next insertion position in the requested direction.
        private ITextPointer GetNextTextSegmentInsertionPosition(LogicalDirection direction)
        {
            ITextSelection thisSelection = (ITextSelection)this; 

            // Move one over symbol in a given direction 
            return thisSelection.MovingPosition.GetNextInsertionPosition(direction); 
        }
 
        bool ITextSelection.Contains(Point point)
        {
            ITextSelection thisSelection = (ITextSelection)this;
 
            if (thisSelection.IsEmpty)
            { 
                return false; 
            }
 
            if (this.TextView == null || !this.TextView.IsValid)
            {
                return false;
            } 

            bool contains = false; 
 
            ITextPointer position = this.TextView.GetTextPositionFromPoint(point, /*snapToText:*/false);
 
            // Did we hit any text?
            if (position != null && thisSelection.Contains(position))
            {
                // If we did, make sure the range covers at least one full character. 
                // Check both character edges.
 
                position = position.GetNextInsertionPosition(position.LogicalDirection); 

                if (position != null && thisSelection.Contains(position)) 
                {
                    contains = true;
                }
            } 

            // Point snapped to text did not hit anything, but we still have a chance 
            // to hit selection - in inter-paragraph or end-of-paragraph areas - highlighted by selection 
            if (!contains)
            { 
                if (_caretElement != null && _caretElement.SelectionGeometry != null &&
                    _caretElement.SelectionGeometry.FillContains(point))
                {
                    contains = true; 
                }
            } 
 
            return contains;
        } 

        #region ITextSelection Implementation

        //...................................................... 
        //
        //  Interaction with Selection host 
        // 
        //......................................................
 

        // Called by TextEditor.OnDetach, when the behavior is shut down.
        void ITextSelection.OnDetach()
        { 
            ITextSelection thisSelection = (ITextSelection)this;
 
            // Check if we need to deactivate the selection 
            thisSelection.UpdateCaretAndHighlight();
 
            // Delete highlight layer created for this selection (if any)
            if (_highlightLayer != null && thisSelection.Start.TextContainer.Highlights.GetLayer(typeof(TextSelection)) == _highlightLayer)
            {
                thisSelection.Start.TextContainer.Highlights.RemoveLayer(_highlightLayer); 
            }
            _highlightLayer = null; 
 
            // Detach the selection from its editor
            _textEditor = null; 
        }

        // ITextView.Updated event listener.
        // Called by the TextEditor. 
        void ITextSelection.OnTextViewUpdated()
        { 
            if ((this.UiScope.IsKeyboardFocused || this.UiScope.IsFocused)) 
            {
                // Use the locally defined caretElement because _caretElement can be null by 
                // detaching CaretElement object
                // Stress bug#1583327 indicate that _caretElement can be set to null by
                // detaching. So the below code is caching the caret element instance locally.
                CaretElement caretElement = _caretElement; 
                if (caretElement != null)
                { 
                    caretElement.OnTextViewUpdated(); 
                }
            } 
        }

        // Perform any cleanup necessary when removing the current UiScope
        // from the visual tree (eg, during a template change). 
        void ITextSelection.DetachFromVisualTree()
        { 
            DetachCaretFromVisualTree(); 
        }
 
        // Italic command event handler. Called by TextEditor.
        void ITextSelection.RefreshCaret()
        {
            // Update the caret to show it as italic or normal caret. 
            RefreshCaret(_textEditor, _textEditor.Selection);
        } 
 
        // This is called from TextStore when the InterimSelection style of the current selection is changed.
        void ITextSelection.OnInterimSelectionChanged(bool interimSelection) 
        {
            // When the interim selection is changed, we need to update the highlight.
            _highlightLayer.InternalOnSelectionChanged();
 
            // Update the caret to show or remove the interim block caret.
            UpdateCaretState(CaretScrollMethod.None); 
        } 

        //...................................................... 
        //
        //  Selection Heuristics
        //
        //...................................................... 

 
        // Moves the selection to the mouse cursor position. 
        // Extends the active end if extend == true, otherwise the selection
        // is collapsed to a caret. 
        void ITextSelection.SetSelectionByMouse(ITextPointer cursorPosition, Point cursorMousePoint)
        {
            ITextSelection thisSelection = (ITextSelection)this;
 
            if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0)
            { 
                // Shift modifier pressed - extending selection from existing one 
                thisSelection.ExtendSelectionByMouse(cursorPosition, /*forceWordSelection:*/false, /*forceParagraphSelection*/false);
            } 
            else
            {
                // No shift modifier pressed - move selection to a cursor position
                MoveSelectionByMouse(cursorPosition, cursorMousePoint); 
            }
        } 
 
        // Extends the selection to the mouse cursor position.
        void ITextSelection.ExtendSelectionByMouse(ITextPointer cursorPosition, bool forceWordSelection, bool forceParagraphSelection) 
        {
            ITextSelection thisSelection = (ITextSelection)this;

            // Check whether the cursor has been actually moved - compare with the previous position 
            if (forceParagraphSelection || _previousCursorPosition != null && cursorPosition.CompareTo(_previousCursorPosition) == 0)
            { 
                // Mouse was not actually moved. Ignore the event. 
                return;
            } 

            thisSelection.BeginChange();
            try
            { 
                if (!BeginMouseSelectionProcess(cursorPosition))
                { 
                    return; 
                }
 
                // Get anchor position
                ITextPointer anchorPosition = ((ITextSelection)this).AnchorPosition;

                // Identify words on selection ends 
                TextSegment anchorWordRange;
                TextSegment cursorWordRange; 
                IdentifyWordsOnSelectionEnds(anchorPosition, cursorPosition, forceWordSelection, out anchorWordRange, out cursorWordRange); 

                // Calculate selection boundary positions 
                ITextPointer startPosition;
                ITextPointer movingPosition;
                if (anchorWordRange.Start.CompareTo(cursorWordRange.Start) <= 0)
                { 
                    startPosition = anchorWordRange.Start.GetFrozenPointer(LogicalDirection.Forward);
                    movingPosition = cursorWordRange.End.GetFrozenPointer(LogicalDirection.Backward); ; 
                } 
                else
                { 
                    startPosition = anchorWordRange.End.GetFrozenPointer(LogicalDirection.Backward);
                    movingPosition = cursorWordRange.Start.GetFrozenPointer(LogicalDirection.Forward); ;
                }
 
                // Note that we use includeCellAtMovingPosition=true because we want that hit-tested table cell
                // be included into selection no matter whether it's empty or not. 
                TextRangeBase.Select(this, startPosition, movingPosition, /*includeCellAtMovingPosition:*/true); 
                SetActivePositions(anchorPosition, movingPosition);
 
                // Store previous cursor position - for the next extension event
                _previousCursorPosition = cursorPosition.CreatePointer();

                Invariant.Assert(thisSelection.Contains(thisSelection.AnchorPosition)); 
            }
            finally 
            { 
                thisSelection.EndChange();
            } 
        }

        // Part of ExtendSelectionByMouse method:
        // Checks whether selection has been started and initiates selection process 
        // Usually always returns true,
        // returns false only as a special value indicating that we need to return without executing selection expansion code. 
        private bool BeginMouseSelectionProcess(ITextPointer cursorPosition) 
        {
            if (_previousCursorPosition == null) 
            {
                // This is a beginning of mouse selection guesture.
                // Initialize the guesture state
                // initially autoword expansion of both ends is enabled 
                _anchorWordRangeHasBeenCrossedOnce = false;
                _allowWordExpansionOnAnchorEnd = true; 
                _reenterPosition = null; 

                if (this.GetUIElementSelected() != null) 
                {
                    // This means that we have just received mousedown event and selected embedded element in this event.
                    // MoveMove event is sent immediately, but we don't want to loose UIElement selection,
                    // so we do not extend to the cursorPosition now. 
                    _previousCursorPosition = cursorPosition.CreatePointer();
                    return false; 
                } 
            }
 
            return true;
        }

        // Part of ExtendSelectionByMouse method: 
        // Identifies words on selection ends.
        private void IdentifyWordsOnSelectionEnds(ITextPointer anchorPosition, ITextPointer cursorPosition, bool forceWordSelection, out TextSegment anchorWordRange, out TextSegment cursorWordRange) 
        { 
            if (forceWordSelection)
            { 
                anchorWordRange = TextPointerBase.GetWordRange(anchorPosition);
                cursorWordRange = TextPointerBase.GetWordRange(cursorPosition, cursorPosition.LogicalDirection);
            }
            else 
            {
                // Define whether word adjustment is allowed. Pressing Shift+Control prevents from auto-word expansion. 
                bool disableWordExpansion = _textEditor.AutoWordSelection == false || ((Keyboard.Modifiers & ModifierKeys.Shift) != 0 && (Keyboard.Modifiers & ModifierKeys.Control) != 0); 

                if (disableWordExpansion) 
                {
                    anchorWordRange = new TextSegment(anchorPosition, anchorPosition);
                    cursorWordRange = new TextSegment(cursorPosition, cursorPosition);
                } 
                else
                { 
                    // Autoword expansion heuristics 
                    // -----------------------------
 
                    // Word autoword heuristics:
                    // a) After active end returned to selected area, autoword expansion on active end is disabled
                    // b) After active end returned to the very first word, expansion on anchor word is disabled either
                    //    We do this though only if selection has crossed initial word boundary at least once. 
                    // c) After active end crosses new word, autoword expansion of active end is enabled again
 
                    // Calculate a word range for anchor position 
                    anchorWordRange = TextPointerBase.GetWordRange(anchorPosition);
 
                    // Check if we re-entering selection or moving outside
                    // and set autoexpansion flags accordingly
                    if (_previousCursorPosition != null &&
                        (anchorPosition.CompareTo(cursorPosition) < 0 && cursorPosition.CompareTo(_previousCursorPosition) < 0 || 
                        _previousCursorPosition.CompareTo(cursorPosition) < 0 && cursorPosition.CompareTo(anchorPosition) < 0))
                    { 
                        // Re-entering selection. 

                        // Store position of reentering 
                        _reenterPosition = cursorPosition.CreatePointer();

                        // When re-entering reaches initial word, disable word expansion on anchor end either
                        if (_anchorWordRangeHasBeenCrossedOnce && anchorWordRange.Contains(cursorPosition)) 
                        {
                            _allowWordExpansionOnAnchorEnd = false; 
                        } 
                    }
                    else 
                    {
                        // Extending the selection.

                        // Check if we are crossing a boundary of last reentered word to re-enable word expansion on moving end 
                        if (_reenterPosition != null)
                        { 
                            TextSegment lastReenteredWordRange = TextPointerBase.GetWordRange(_reenterPosition); 
                            if (!lastReenteredWordRange.Contains(cursorPosition))
                            { 
                                _reenterPosition = null;
                            }
                        }
                    } 

                    // Identify expanded range on both ends 
                    // 

                    if (anchorWordRange.Contains(cursorPosition) || 
                        anchorWordRange.Contains(cursorPosition.GetInsertionPosition(LogicalDirection.Forward)) ||
                        anchorWordRange.Contains(cursorPosition.GetInsertionPosition(LogicalDirection.Backward)))
                    {
                        // Selection does not cross word boundary, so shrink selection to exact anchor/cursor positions 
                        anchorWordRange = new TextSegment(anchorPosition, anchorPosition);
                        cursorWordRange = new TextSegment(cursorPosition, cursorPosition); 
                    } 
                    else
                    { 
                        // Selection crosses word boundary.
                        _anchorWordRangeHasBeenCrossedOnce = true;

                        if (!_allowWordExpansionOnAnchorEnd || // 
                            TextPointerBase.IsAtWordBoundary(anchorPosition, /*insideWordDirection:*/LogicalDirection.Forward))
                        { 
                            // We collapse anchorPosition in two cases: 
                            // If we have been re-entering the initial word before -
                            // then we treat it as an indicator that user wants exact position on anchor end 
                            // or
                            // if selection starts exactly on word boundary -
                            // then we should not include the following word (when selection extends backward).
                            // 
                            // So in the both cases we collapse anchorWordRange to exact _anchorPosition
                            anchorWordRange = new TextSegment(anchorPosition, anchorPosition); 
                        } 

                        if (TextPointerBase.IsAfterLastParagraph(cursorPosition) || 
                            TextPointerBase.IsAtWordBoundary(cursorPosition, /*insideWordDirection:*/LogicalDirection.Forward))
                        {
                            cursorWordRange = new TextSegment(cursorPosition, cursorPosition);
                        } 
                        else
                        { 
                            if (_reenterPosition == null) 
                            {
                                // We are not in re-entering mode; expand moving end to word boundary 
                                cursorWordRange = TextPointerBase.GetWordRange(cursorPosition, cursorPosition.LogicalDirection);
                            }
                            else
                            { 
                                // We are in re-entering mode; use exact moving end position
                                cursorWordRange = new TextSegment(cursorPosition, cursorPosition); 
                            } 
                        }
                    } 
                }
            }
        }
 
        //......................................................
        // 
        //  Table Selection 
        //
        //...................................................... 

        /// 
        /// Extends table selection by one row in a given direction
        ///  
        /// 
        /// LogicalDirection.Forward means moving active cell one row down, 
        /// LogicalDirection.Backward - one row up. 
        /// 
        bool ITextSelection.ExtendToNextTableRow(LogicalDirection direction) 
        {
            TableCell anchorCell;
            TableCell movingCell;
            TableRowGroup rowGroup; 
            int nextRowIndex;
            TableCell nextCell; 
 
            if (!this.IsTableCellRange)
            { 
                return false;
            }

            Invariant.Assert(!this.IsEmpty); 
            Invariant.Assert(_anchorPosition != null);
            Invariant.Assert(_movingPositionEdge != MovingEdge.None); 
 
            if (!TextRangeEditTables.IsTableCellRange((TextPointer)_anchorPosition, (TextPointer)((ITextSelection)this).MovingPosition, /*includeCellAtMovingPosition:*/false, out anchorCell, out movingCell))
            { 
                return false;
            }

            Invariant.Assert(anchorCell != null && movingCell != null); 

            rowGroup = movingCell.Row.RowGroup; 
 
            nextCell = null;
 
            if (direction == LogicalDirection.Forward)
            {
                // Move movingPosition to the following row.
                // Find a row in the forward direction 
                nextRowIndex = movingCell.Row.Index + movingCell.RowSpan;
                if (nextRowIndex < rowGroup.Rows.Count) 
                { 
                    nextCell = FindCellAtColumnIndex(rowGroup.Rows[nextRowIndex].Cells, movingCell.ColumnIndex);
                } 
            }
            else
            {
                // Find preceding row containing a cell in position of movingCell's first column 
                nextRowIndex = movingCell.Row.Index - 1;
                while (nextRowIndex >= 0) 
                { 
                    nextCell = FindCellAtColumnIndex(rowGroup.Rows[nextRowIndex].Cells, movingCell.ColumnIndex);
                    if (nextCell != null) 
                    {
                        break;
                    }
                    nextRowIndex--; 
                }
            } 
 
            if (nextCell != null)
            { 
                //
                ITextPointer movingPosition = nextCell.ContentEnd.CreatePointer();
                movingPosition.MoveToNextInsertionPosition(LogicalDirection.Forward);
 
                TextRangeBase.Select(this, _anchorPosition, movingPosition);
 
                // Make sure that active positions are inside of a selection 
                SetActivePositions(_anchorPosition, movingPosition);
 
                return true;
            }

            return false; 
        }
 
        //------------------------------------------------------ 
        //
        //  ITextSelection Properties 
        //
        //-----------------------------------------------------

        // True if the current seleciton is for interim character. 
        // Korean Interim character is now invisilbe selection (no highlight) and the controls needs to
        // have the block caret to indicate the interim character. 
        // This should be updated by TextStore. 
        internal bool IsInterimSelection
        { 
            get
            {
                if (this.TextStore != null)
                { 
                    return TextStore.IsInterimSelection;
                } 
 
                return false;
            } 
        }

        // IsInterimSelection wrapper for ITextSelection.
        bool ITextSelection.IsInterimSelection 
        {
            get 
            { 
                return this.IsInterimSelection;
            } 
        }

        #endregion ITextSelection Implementation
 

        // ***************************************************** 
        // ***************************************************** 
        // ******************************************************
        // 
        // Concrete TextSelection Implementation
        //
        // *****************************************************
        // ****************************************************** 
        // ******************************************************
 
 
        //-----------------------------------------------------
        // 
        //  Public Methods
        //
        //------------------------------------------------------
 
        //......................................................
        // 
        //  Selection extension (moving active end) 
        //
        //...................................................... 

        /// 
        /// Potential public method - concrete equivalent of abstract method
        ///  
        internal TextPointer AnchorPosition
        { 
            get 
            {
                // ATTENTION: This method is supposed to be a pure redirection 
                // to corresponding ITextSelection method - to keep abstract and concrete selection behavior consistent
                return (TextPointer)((ITextSelection)this).AnchorPosition;
            }
        } 

        ///  
        /// Potential public method - concrete equivalent of abstract method 
        /// 
        internal TextPointer MovingPosition 
        {
            get
            {
                // ATTENTION: This method is supposed to be a pure redirection 
                // to corresponding ITextSelection method - to keep abstract and concrete selection behavior consistent
                return (TextPointer)((ITextSelection)this).MovingPosition; 
            } 
        }
 
        /// 
        /// Potential public method - concrete equivalent of abstract method
        /// 
        internal void SetCaretToPosition(TextPointer caretPosition, LogicalDirection direction, bool allowStopAtLineEnd, bool allowStopNearSpace) 
        {
            // ATTENTION: This method is supposed to be a pure redirection 
            // to corresponding ITextSelection method - to keep abstract and concrete selection behavior consistent 
            ((ITextSelection)this).SetCaretToPosition(caretPosition, direction, allowStopAtLineEnd, allowStopNearSpace);
        } 

        //
        internal bool ExtendToNextInsertionPosition(LogicalDirection direction)
        { 
            // ATTENTION: This method is supposed to be a pure redirection
            // to corresponding ITextSelection method - to keep abstract and concrete selection behavior consistent 
            return ((ITextSelection)this).ExtendToNextInsertionPosition(direction); 
        }
 
        #endregion Public Methods

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

        // InputLanguageChanged event handler. Called by TextEditor.
        internal static void OnInputLanguageChanged(CultureInfo cultureInfo)
        { 
            TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore;
 
            // Check the changed input language of the cultureInfo to draw the BiDi caret in case of BiDi language 
            // like as Arabic or Hebrew input language.
            if (IsBidiInputLanguage(cultureInfo)) 
            {
                threadLocalStore.Bidi = true;
            }
            else 
            {
                threadLocalStore.Bidi = false; 
            } 

            // Update caret on focused text editor 
            if (threadLocalStore.FocusedTextSelection != null)
            {
                ((ITextSelection)threadLocalStore.FocusedTextSelection).RefreshCaret();
            } 
        }
 
        // Returns true if the text matching a pixel position falls within 
        // the selection.
        internal bool Contains(Point point) 
        {
            return ((ITextSelection)this).Contains(point);
        }
 
        //-----------------------------------------------------
        // 
        // Internal Virtual Methods - TextSelection Extensibility 
        //
        //------------------------------------------------------ 

        //......................................................
        //
        //  Formatting 
        //
        //...................................................... 
 
        /// 
        ///  Append an object at the end of the TextRange. 
        /// 
        internal override void InsertEmbeddedUIElementVirtual(FrameworkElement embeddedElement)
        {
            TextRangeBase.BeginChange(this); 
            try
            { 
                base.InsertEmbeddedUIElementVirtual(embeddedElement); 

                this.ClearSpringloadFormatting(); 
            }
            finally
            {
                TextRangeBase.EndChange(this); 
            }
        } 
 
        /// 
        /// Applies a property value to a selection. 
        /// In case of empty selection sets property to springloaded property set.
        /// 
        internal override void ApplyPropertyToTextVirtual(DependencyProperty formattingProperty, object value, bool applyToParagraphs, PropertyValueAction propertyValueAction)
        { 
            if (!TextSchema.IsParagraphProperty(formattingProperty) && !TextSchema.IsCharacterProperty(formattingProperty))
            { 
                return; // Ignore any unknown property 
            }
 
            // Check whether we are in a situation when auto-word formatting must happen
            if (this.IsEmpty && TextSchema.IsCharacterProperty(formattingProperty) &&
                !applyToParagraphs &&
                formattingProperty != FrameworkElement.FlowDirectionProperty) // We dont want to apply flowdirection property to inlines when selection is empty. 
            {
                TextSegment autoWordRange = TextRangeBase.GetAutoWord(this); 
                if (autoWordRange.IsNull) 
                {
                    // This property goes to springload formatting. We should not create undo unit for it. 
                    if (_springloadFormatting == null)
                    {
                        _springloadFormatting = new DependencyObject();
                    } 

                    _springloadFormatting.SetValue(formattingProperty, value); 
                } 
                else
                { 
                    // TextRange will create undo unit with proper name
                    new TextRange(autoWordRange.Start, autoWordRange.End).ApplyPropertyValue(formattingProperty, value);
                }
            } 
            else
            { 
                // No word to auto-format. Apply property to a selection. 
                // TextRange will create undo unit with proper name
                base.ApplyPropertyToTextVirtual(formattingProperty, value, applyToParagraphs, propertyValueAction); 
                this.ClearSpringloadFormatting();
            }
        }
 
        internal override void ClearAllPropertiesVirtual()
        { 
            // Character property - applies to text runs in selected range, or springloaded if selection is empty 
            if (this.IsEmpty)
            { 
                this.ClearSpringloadFormatting();
            }
            else
            { 
                TextRangeBase.BeginChange(this);
                try 
                { 
                    base.ClearAllPropertiesVirtual();
 
                    this.ClearSpringloadFormatting();
                }
                finally
                { 
                    TextRangeBase.EndChange(this);
                } 
            } 
        }
 
        //......................................................
        //
        //  Range Serialization
        // 
        //......................................................
 
        // Worker for Xml property setter; enables extensibility for TextSelection 
        internal override void SetXmlVirtual(TextElement fragment)
        { 
            TextRangeBase.BeginChange(this);
            try
            {
                base.SetXmlVirtual(fragment); 

                this.ClearSpringloadFormatting(); 
            } 
            finally
            { 
                TextRangeBase.EndChange(this);
            }
        }
 
        // Worker for Load public method; enables extensibility for TextSelection
        internal override void LoadVirtual(Stream stream, string dataFormat) 
        { 
            TextRangeBase.BeginChange(this);
            try 
            {
                base.LoadVirtual(stream, dataFormat);

                this.ClearSpringloadFormatting(); 
            }
            finally 
            { 
                TextRangeBase.EndChange(this);
            } 
        }

        //......................................................
        // 
        //  Table Editing
        // 
        //...................................................... 

        // Worker for InsertTable; enables extensibility for TextSelection 
        internal override Table InsertTableVirtual(int rowCount, int columnCount)
        {
            using (DeclareChangeBlock())
            { 
                Table table = base.InsertTableVirtual(rowCount, columnCount);
 
                if (table != null) 
                {
                    TextPointer cellStart = table.RowGroups[0].Rows[0].Cells[0].ContentStart; 

                    this.SetCaretToPosition(cellStart, LogicalDirection.Backward, /*allowStopAtLineEnd:*/false, /*allowStopNearSpace:*/false);
                }
 
                return table;
            } 
        } 

        // .............................................................. 
        //
        // Springload Formatting
        //
        // .............................................................. 

        #region Springload Formatting 
 
        /// 
        /// Function used in OnApplyProperty commands for character formatting properties. 
        /// It takes care of springload formatting (applied to empty selection).
        /// 
        /// 
        /// Property whose value is subject to be toggled. 
        /// 
        ///  
        /// The value of a property 
        /// 
        internal object GetCurrentValue(DependencyProperty formattingProperty) 
        {
            ITextSelection thisSelection = this;
            object propertyValue = DependencyProperty.UnsetValue;
 
            if (thisSelection.Start is TextPointer)
            { 
                if (_springloadFormatting != null && this.IsEmpty) 
                {
                    // Get springload value 
                    propertyValue = _springloadFormatting.ReadLocalValue(formattingProperty);
                    if (propertyValue == DependencyProperty.UnsetValue)
                    {
                        propertyValue = this.Start.Parent.GetValue(formattingProperty); 
                    }
                } 
            } 

            // If there's no spring loaded value, read the local value. 
            if (propertyValue == DependencyProperty.UnsetValue)
            {
                propertyValue = this.PropertyPosition.GetValue(formattingProperty);
            } 

            return propertyValue; 
        } 

        ///  
        /// Reads a set of current formatting properties (from selection start)
        /// into _springloadFormatting - to apply potentially for the following input.
        /// 
        internal void SpringloadCurrentFormatting() 
        {
            if (((ITextSelection)this).Start is TextPointer) 
            { 
                TextPointer start = this.Start;
 
                Inline ancestor = start.GetNonMergeableInlineAncestor();
                if (ancestor != null)
                {
                    // Unless the selection is wholly contained within a Hyperlink, we don't 
                    // want to springload its character properties.
                    if (this.End.GetNonMergeableInlineAncestor() != ancestor) 
                    { 
                        start = ancestor.ElementEnd;
                    } 
                }

                if (_springloadFormatting == null)
                { 
                    SpringloadCurrentFormatting(start.Parent);
                } 
            } 
        }
 
        private void SpringloadCurrentFormatting(DependencyObject parent)
        {
            // Create new bag for formatting properties
            _springloadFormatting = new DependencyObject(); 

            // Check if we have an object to read from 
            if (parent == null) 
            {
                return; 
            }

            DependencyProperty[] inheritableProperties = TextSchema.GetInheritableProperties(typeof(Inline));
            DependencyProperty[] noninheritableProperties = TextSchema.GetNoninheritableProperties(typeof(Span)); 

            // Walk up the tree.  At each step, if the element is typographical only, 
            // grab all non-inherited values.  When we reach the top of the tree, grab all 
            // values.
            DependencyObject element = parent; 
            while (element is Inline)
            {
                TextElementEditingBehaviorAttribute att = (TextElementEditingBehaviorAttribute)Attribute.GetCustomAttribute(element.GetType(), typeof(TextElementEditingBehaviorAttribute));
                if (att.IsTypographicOnly) 
                {
                    for (int i = 0; i < inheritableProperties.Length; i++) 
                    { 
                        if (_springloadFormatting.ReadLocalValue(inheritableProperties[i]) == DependencyProperty.UnsetValue &&
                            inheritableProperties[i] != FrameworkElement.LanguageProperty && 
                            inheritableProperties[i] != FrameworkElement.FlowDirectionProperty &&
                            System.Windows.DependencyPropertyHelper.GetValueSource(element, inheritableProperties[i]).BaseValueSource != BaseValueSource.Inherited)
                        {
                            object value = parent.GetValue(inheritableProperties[i]); 
                            _springloadFormatting.SetValue(inheritableProperties[i], value);
                        } 
                    } 
                    for (int i = 0; i < noninheritableProperties.Length; i++)
                    { 
                        if (_springloadFormatting.ReadLocalValue(noninheritableProperties[i]) == DependencyProperty.UnsetValue &&
                            noninheritableProperties[i] != TextElement.TextEffectsProperty &&
                            System.Windows.DependencyPropertyHelper.GetValueSource(element, noninheritableProperties[i]).BaseValueSource != BaseValueSource.Inherited)
                        { 
                            object value = parent.GetValue(noninheritableProperties[i]);
                            _springloadFormatting.SetValue(noninheritableProperties[i], value); 
                        } 
                    }
                } 
                element = ((TextElement)element).Parent;
            }
        }
 
        /// 
        /// Clears springload formatting, so that the following text input 
        /// will use formatting from a current position. 
        /// 
        internal void ClearSpringloadFormatting() 
        {
            if (((ITextSelection)this).Start is TextPointer)
            {
                // Delete all springloaded values 
                _springloadFormatting = null;
 
                // Update caret italic state 
                ((ITextSelection)this).RefreshCaret();
            } 
        }

        /// 
        /// Applies springload formatting to a given content range. 
        /// Clears springloadFormatting after applying it.
        ///  
        internal void ApplySpringloadFormatting() 
        {
            if (!(((ITextSelection)this).Start is TextPointer)) 
            {
                return;
            }
 
            if (this.IsEmpty)
            { 
                // We can't apply formatting to non-TextContainers or empty selection. 
                return;
            } 

            if (_springloadFormatting != null)
            {
                Invariant.Assert(this.Start.LogicalDirection == LogicalDirection.Backward); 
                Invariant.Assert(this.End.LogicalDirection == LogicalDirection.Forward);
 
                LocalValueEnumerator springloadFormattingValues = _springloadFormatting.GetLocalValueEnumerator(); 

                while (!this.IsEmpty && springloadFormattingValues.MoveNext()) 
                {
                    // Note: we repeatedly check for IsEmpty because the selection
                    // may become empty as a result of normalization after formatting
                    // (thai character sequence). 
                    LocalValueEntry propertyEntry = springloadFormattingValues.Current;
                    Invariant.Assert(TextSchema.IsCharacterProperty(propertyEntry.Property)); 
                    base.ApplyPropertyValue(propertyEntry.Property, propertyEntry.Value); 
                }
 
                ClearSpringloadFormatting();
            }
        }
 
        #endregion Springload Formatting
 
        #region Caret Support 

        // .............................................................. 
        //
        // Caret Support
        //
        // .............................................................. 

        // Shows/hides the caret and scrolls it into view if requested. 
        // Called when the range is moved or layout is updated. 
        internal void UpdateCaretState(CaretScrollMethod caretScrollMethod)
        { 
            Invariant.Assert(caretScrollMethod != CaretScrollMethod.Unset);

            if (_pendingCaretNavigation)
            { 
                caretScrollMethod = CaretScrollMethod.Navigation;
                _pendingCaretNavigation = false; 
            } 

            if (_caretScrollMethod == CaretScrollMethod.Unset) 
            {
                _caretScrollMethod = caretScrollMethod;

                // Post a "Loaded" priority operation to the dispatcher queue. 

                // Operations at Loaded priority are processed when layout and render is 
                // done but just before items at input priority are serviced. 
                // We want the update caret worker to run after layout is clean.
                if (_textEditor.TextView != null && _textEditor.TextView.IsValid) 
                {
                    UpdateCaretStateWorker(null);
                }
                else 
                {
                    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(UpdateCaretStateWorker), null); 
                } 
            }
            else if (caretScrollMethod != CaretScrollMethod.None) 
            {
                _caretScrollMethod = caretScrollMethod;
            }
        } 

        // Get the caret brush that is the inverted color from the system window or background color. 
        internal static Brush GetCaretBrush(TextEditor textEditor, byte opacity) 
        {
            Color backgroundColor; 
            ITextSelection focusedTextSelection;
            object backgroundPropertyValue;

            // Get the default background from the system color or UiScope's background 
            backgroundPropertyValue = textEditor.UiScope.GetValue(System.Windows.Controls.Panel.BackgroundProperty);
            if (backgroundPropertyValue != null && backgroundPropertyValue != DependencyProperty.UnsetValue && 
                backgroundPropertyValue is SolidColorBrush) 
            {
                backgroundColor = ((SolidColorBrush)backgroundPropertyValue).Color; 
            }
            else
            {
                backgroundColor = SystemColors.WindowColor; 
            }
 
            // Get the background color from current selection 
            focusedTextSelection = textEditor.Selection;
            if (focusedTextSelection is TextSelection) 
            {
                backgroundPropertyValue = ((TextSelection)focusedTextSelection).GetCurrentValue(TextElement.BackgroundProperty);
                if (backgroundPropertyValue != null && backgroundPropertyValue != DependencyProperty.UnsetValue)
                { 
                    if (backgroundPropertyValue is SolidColorBrush)
                    { 
                        backgroundColor = ((SolidColorBrush)backgroundPropertyValue).Color; 
                    }
                } 
            }

            // Invert the color to get the caret color from the system window or background color.
            byte r = (byte)~(backgroundColor.R); 
            byte g = (byte)~(backgroundColor.G);
            byte b = (byte)~(backgroundColor.B); 
 
            return new SolidColorBrush(Color.FromArgb(opacity, r, g, b));
        } 

        #endregion Caret Support

        #region Bidi Support 

        //...................................................... 
        // 
        //  BIDI Support
        // 
        //......................................................

        /// 
        /// Check the installed bidi input language from the current 
        /// input keyboard list.
        ///  
        ///  
        /// Critical - calls unmanaged code to get the current available keyboard list.
        /// TreatAsSafe - data exposed (user using bidi) is safe to release 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal static bool IsBidiInputLanguageInstalled()
        { 
            bool bidiInputLanguageInstalled;
 
            bidiInputLanguageInstalled = false; 

            int keyboardListCount = (int)SafeNativeMethods.GetKeyboardLayoutList(0, null); 
            if (keyboardListCount > 0)
            {
                int keyboardListIndex;
                IntPtr[] keyboardList; 

                keyboardList = new IntPtr[keyboardListCount]; 
 
                keyboardListCount = SafeNativeMethods.GetKeyboardLayoutList(keyboardListCount, keyboardList);
 
                for (keyboardListIndex = 0;
                     (keyboardListIndex < keyboardList.Length) && (keyboardListIndex < keyboardListCount);
                     keyboardListIndex++)
                { 
                    CultureInfo cultureInfo = new CultureInfo((short)keyboardList[keyboardListIndex]);
 
                    if (IsBidiInputLanguage(cultureInfo)) 
                    {
                        bidiInputLanguageInstalled = true; 
                        break;
                    }
                }
            } 

            return bidiInputLanguageInstalled; 
        } 

        #endregion Bidi Support 

        // Forces a synchronous layout validation, up to the selection moving position.
        void ITextSelection.ValidateLayout()
        { 
            ((ITextSelection)this).MovingPosition.ValidateLayout();
        } 
 
        #endregion Internal methods
 
        //-----------------------------------------------------
        //
        //  Internal Properties
        // 
        //------------------------------------------------------
 
        #region Internal Properties 

        // Caret associated with this TextSelection. 
        internal CaretElement CaretElement
        {
            get
            { 
                return _caretElement;
            } 
        } 

        // Caret associated with this TextSelection. 
        CaretElement ITextSelection.CaretElement
        {
            get
            { 
                return this.CaretElement;
            } 
        } 

        // Returns true iff there are no additional insertion positions are either 
        // end of the selection.
        bool ITextSelection.CoversEntireContent
        {
            get 
            {
                ITextSelection This = this; 
 
                return (This.Start.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text &&
                        This.End.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text && 
                        This.Start.GetNextInsertionPosition(LogicalDirection.Backward) == null &&
                        This.End.GetNextInsertionPosition(LogicalDirection.Forward) == null);
            }
        } 

        #endregion Internal Properties 
 
        //------------------------------------------------------
        // 
        //  Private Methods
        //
        //-----------------------------------------------------
 
        #region Private Methods
 
        // Stores this TextSelection into our TLS slot. 
        private void SetThreadSelection()
        { 
            TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore;

            // Store this selection as focused one
            threadLocalStore.FocusedTextSelection = this; 
        }
 
        // Removes this TextSelection from our TLS slot. 
        private void ClearThreadSelection()
        { 
            // Clear currently focused selection, if it's us
            if (TextEditor._ThreadLocalStore.FocusedTextSelection == this)
            {
                TextEditor._ThreadLocalStore.FocusedTextSelection = null; 
            }
        } 
 
        // GotKeyboardFocus event handler.  Called by UpdateCaretAndHighlight.
        private void Highlight() 
        {
            // Make sure that a highlight layer exists for drawing this selection
            if (_highlightLayer == null)
            { 
                _highlightLayer = new TextSelectionHighlightLayer(this);
            } 
 
            // Make selection visible
            if (((ITextSelection)this).Start.TextContainer.Highlights.GetLayer(typeof(TextSelection)) == null) 
            {
                ((ITextSelection)this).Start.TextContainer.Highlights.AddLayer(_highlightLayer);
            }
        } 

        // LostKeyboardFocus event handler.  Called by UpdateCaretAndHighlight. 
        private void Unhighlight() 
        {
            TextSelectionHighlightLayer highlightLayer = ((ITextSelection)this).Start.TextContainer.Highlights.GetLayer(typeof(TextSelection)) as TextSelectionHighlightLayer; 
            if (highlightLayer != null)
            {
                ((ITextSelection)this).Start.TextContainer.Highlights.RemoveLayer(highlightLayer);
                Invariant.Assert(((ITextSelection)this).Start.TextContainer.Highlights.GetLayer(typeof(TextSelection)) == null); 
            }
        } 
 
        //......................................................
        // 
        //  Active Positions of Selection
        //
        //......................................................
 
        /// 
        /// Stores normalized anchor and moving positions for the selection. 
        /// Ensures that they are both inside of range Start/End. 
        /// 
        ///  
        /// A position which must be stored as initial position for the selection.
        /// 
        /// 
        /// The "hot" or active selection edge which responds to user input. 
        /// 
        private void SetActivePositions(ITextPointer anchorPosition, ITextPointer movingPosition) 
        { 
            // The following settings are used in auto-word exapnsion.
            // By setting a _previousPosition we are clearing them all - 
            // they will be re-initialized in the beginning of a selection
            // expansion guesture in ExtendSelectionByMouse method
            // Previous position is needed for selection gestures to remember
            // where mouse drag happed last time. Used for word autoexpansion. 
            _previousCursorPosition = null;
 
            if (this.IsEmpty) 
            {
                _anchorPosition = null; 
                _movingPositionEdge = MovingEdge.None;
                return;
            }
 
            Invariant.Assert(anchorPosition != null);
 
            ITextSelection thisSelection = (ITextSelection)this; 

            // Normalize and store new selection anchor position 
            _anchorPosition = anchorPosition.GetInsertionPosition(anchorPosition.LogicalDirection);

            // Ensure that anchor position is within one of text segments
            if (_anchorPosition.CompareTo(thisSelection.Start) < 0) 
            {
                _anchorPosition = thisSelection.Start.GetFrozenPointer(_anchorPosition.LogicalDirection); 
            } 
            else if (_anchorPosition.CompareTo(thisSelection.End) > 0)
            { 
                _anchorPosition = thisSelection.End.GetFrozenPointer(_anchorPosition.LogicalDirection);
            }

            _movingPositionEdge = ConvertToMovingEdge(anchorPosition, movingPosition); 
            _movingPositionDirection = movingPosition.LogicalDirection;
        } 
 
        // Uses the current selection state to match an ITextPointer to one of the possible
        // moving position edges. 
        private MovingEdge ConvertToMovingEdge(ITextPointer anchorPosition, ITextPointer movingPosition)
        {
            ITextSelection thisSelection = this;
            MovingEdge movingEdge; 

            if (thisSelection.IsEmpty) 
            { 
                // Empty selections have no moving edge.
                movingEdge = MovingEdge.None; 
            }
            else if (thisSelection.TextSegments.Count < 2)
            {
                // Simple text selections move opposite their anchor positions. 
                movingEdge = (anchorPosition.CompareTo(movingPosition) <= 0) ? MovingEdge.End : MovingEdge.Start;
            } 
            else 
            {
                // Table selection.  Look for an exact match. 
                if (movingPosition.CompareTo(thisSelection.Start) == 0)
                {
                    movingEdge = MovingEdge.Start;
                } 
                else if (movingPosition.CompareTo(thisSelection.End) == 0)
                { 
                    movingEdge = MovingEdge.End; 
                }
                else if (movingPosition.CompareTo(thisSelection.TextSegments[0].End) == 0) 
                {
                    movingEdge = MovingEdge.StartInner;
                }
                else if (movingPosition.CompareTo(thisSelection.TextSegments[thisSelection.TextSegments.Count-1].Start) == 0) 
                {
                    movingEdge = MovingEdge.EndInner; 
                } 
                else
                { 
                    movingEdge = (anchorPosition.CompareTo(movingPosition) <= 0) ? MovingEdge.End : MovingEdge.Start;
                }
            }
 
            return movingEdge;
        } 
 
        //......................................................
        // 
        //  Selection Building With Mouse
        //
        //......................................................
 
        // Moves the selection to the mouse cursor position.
        // If the cursor is facing a UIElement, select the UIElement. 
        // Sets new selection anchor to a given cursorPosition. 
        private void MoveSelectionByMouse(ITextPointer cursorPosition, Point cursorMousePoint)
        { 
            ITextSelection thisSelection = (ITextSelection)this;

            if (this.TextView == null)
            { 
                return;
            } 
            Invariant.Assert(this.TextView.IsValid); // We just checked RenderScope. We'll use TextView below 

            ITextPointer movingPosition = null; 

            if (cursorPosition.GetPointerContext(cursorPosition.LogicalDirection) == TextPointerContext.EmbeddedElement)
            {
                Rect objectEdgeRect = this.TextView.GetRectangleFromTextPosition(cursorPosition); 

                // Check for embedded object. 
                // If the click happend inside of it we need to select it as a whole, when content is not read-only. 
                if (!_textEditor.IsReadOnly && ShouldSelectEmbeddedObject(cursorPosition, cursorMousePoint, objectEdgeRect))
                { 
                    movingPosition = cursorPosition.GetNextContextPosition(cursorPosition.LogicalDirection);
                }
            }
 
            // Move selection to this position
            if (movingPosition == null) 
            { 
                thisSelection.SetCaretToPosition(cursorPosition, cursorPosition.LogicalDirection, /*allowStopAtLineEnd:*/true, /*allowStopNearSpace:*/false);
            } 
            else
            {
                thisSelection.Select(cursorPosition, movingPosition);
            } 
        }
 
        // Helper for MoveSelectionByMouse 
        private bool ShouldSelectEmbeddedObject(ITextPointer cursorPosition, Point cursorMousePoint, Rect objectEdgeRect)
        { 
            // Although we now know that cursorPosition is facing an embedded object,
            // we still need an additional test to determine if the original mouse point
            // fell within the object or outside it's bouding box (which can happen when
            // a mouse click is snapped to the nearest content). 
            // If the mouse point is outside the object, we don't want to select it.
            if (!objectEdgeRect.IsEmpty && 
                cursorMousePoint.Y >= objectEdgeRect.Y && cursorMousePoint.Y < objectEdgeRect.Y + objectEdgeRect.Height) 
            {
                // Compare X coordinates of mouse down point and object edge rect, 
                // depending on the FlowDirection of the render scope and paragraph content.

                FlowDirection renderScopeFlowDirection = (FlowDirection)this.TextView.RenderScope.GetValue(Block.FlowDirectionProperty);
                FlowDirection paragraphFlowDirection = (FlowDirection)cursorPosition.GetValue(Block.FlowDirectionProperty); 

                if (renderScopeFlowDirection == FlowDirection.LeftToRight) 
                { 
                    if (paragraphFlowDirection == FlowDirection.LeftToRight &&
                        (cursorPosition.LogicalDirection == LogicalDirection.Forward && objectEdgeRect.X < cursorMousePoint.X || 
                        cursorPosition.LogicalDirection == LogicalDirection.Backward && cursorMousePoint.X < objectEdgeRect.X))
                    {
                        return true;
                    } 
                    else if (paragraphFlowDirection == FlowDirection.RightToLeft &&
                        (cursorPosition.LogicalDirection == LogicalDirection.Forward && objectEdgeRect.X > cursorMousePoint.X || 
                        cursorPosition.LogicalDirection == LogicalDirection.Backward && cursorMousePoint.X > objectEdgeRect.X)) 
                    {
                        return true; 
                    }
                }
                else
                { 
                    if (paragraphFlowDirection == FlowDirection.LeftToRight &&
                        (cursorPosition.LogicalDirection == LogicalDirection.Forward && objectEdgeRect.X > cursorMousePoint.X || 
                        cursorPosition.LogicalDirection == LogicalDirection.Backward && cursorMousePoint.X > objectEdgeRect.X)) 
                    {
                        return true; 
                    }
                    else if (paragraphFlowDirection == FlowDirection.RightToLeft &&
                        (cursorPosition.LogicalDirection == LogicalDirection.Forward && objectEdgeRect.X < cursorMousePoint.X ||
                        cursorPosition.LogicalDirection == LogicalDirection.Backward && cursorMousePoint.X < objectEdgeRect.X)) 
                    {
                        return true; 
                    } 
                }
            } 

            return false;
        }
 
        //......................................................
        // 
        //  Caret Support 
        //
        //...................................................... 

        // Redraws a caret using current setting for italic - taking springload formatting into account.
        private static void RefreshCaret(TextEditor textEditor, ITextSelection textSelection)
        { 
            object fontStylePropertyValue;
            bool italic; 
 
            if (textSelection == null || textSelection.CaretElement == null)
            { 
                return;
            }

            // NOTE: We are using GetCurrentValue to take springload formatting into account. 
            fontStylePropertyValue = ((TextSelection)textSelection).GetCurrentValue(TextElement.FontStyleProperty);
            italic = (textEditor.AcceptsRichContent && fontStylePropertyValue != DependencyProperty.UnsetValue && (FontStyle)fontStylePropertyValue == FontStyles.Italic); 
 
            textSelection.CaretElement.RefreshCaret(italic);
        } 

        // Called after a caret navigation, to signal that the next caret
        // scroll-into-view should include hueristics to include following
        // text. 
        internal void OnCaretNavigation()
        { 
            _pendingCaretNavigation = true; 
        }
 
        // Called after a caret navigation, to signal that the next caret
        // scroll-into-view should include hueristics to include following
        // text.
        void ITextSelection.OnCaretNavigation() 
        {
            OnCaretNavigation(); 
        } 

        // Callback for UpdateCaretState worker. 
        private object UpdateCaretStateWorker(object o)
        {
            // This can happen if selection has been detached by TextEditor.OnDetach.
            if (_textEditor == null) 
            {
                return null; 
            } 

            TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore; 

            CaretScrollMethod caretScrollMethod = _caretScrollMethod;
            _caretScrollMethod = CaretScrollMethod.Unset;
 
            // Use the locally defined caretElement because _caretElement can be null by
            // detaching CaretElement object 
            CaretElement caretElement = _caretElement; 

            if (caretElement == null) 
            {
                return null;
            }
 
            if (threadLocalStore.FocusedTextSelection == null)
            { 
                // If we have multiple windows open, a non-blinking caret might be showing 
                // in tne given TextEditor's UiScope.  If the selection for that Editor is
                // not empty, we need to hide the caret. 
                if (!this.IsEmpty)
                {
                    caretElement.Hide();
                } 

                return null; 
            } 

            // When the TextView is not valid, there is nothing to do 
            if (_textEditor.TextView == null || !_textEditor.TextView.IsValid)
            {
                //
                return null; 
            }
 
            if (!this.VerifyAdornerLayerExists()) 
            {
                caretElement.Hide(); 
            }

            // Identify caret position
            // Make sure that moving position is inside of selection 
            ITextPointer caretPosition = IdentifyCaretPosition(this);
 
            // If caret position is not valid in focusedTextSelection.TextView, we cannot update it. Return 
            if (caretPosition.HasValidLayout)
            { 
                Rect caretRectangle;
                bool italic = false;
                bool caretVisible = (this.IsEmpty && !_textEditor.IsReadOnly) || (_textEditor.IsReadOnly && _textEditor.IsReadOnlyCaretVisible);
 
                if (!this.IsInterimSelection)
                { 
                    caretRectangle = CalculateCaretRectangle(this, caretPosition); 

                    if (this.IsEmpty) 
                    {
                        // Identify italic condition (including springload) - to use for caret shaping
                        object fontStylePropertyValue = GetPropertyValue(TextElement.FontStyleProperty);
                        italic = (_textEditor.AcceptsRichContent && fontStylePropertyValue != DependencyProperty.UnsetValue && (FontStyle)fontStylePropertyValue == FontStyles.Italic); 
                    }
                } 
                else 
                {
                    caretRectangle = CalculateInterimCaretRectangle(this); 

                    // Caret always visible on the interim input mode.
                    caretVisible = true;
                } 

                Brush caretBrush = GetCaretBrush(_textEditor, /*opacity:1.0f*/0xff); 
 
                // Calculate the scroll origin position to scroll it with the caret position.
                double scrollToOriginPosition = CalculateScrollToOriginPosition(_textEditor, caretPosition, caretRectangle.X); 

                // Re-render the caret.
                // Get a bounding rect from the active end of selection.
                caretElement.Update(caretVisible, caretRectangle, caretBrush, italic, caretScrollMethod, scrollToOriginPosition); 
            }
 
            //  CaretElement.Update(...) makes a conditional call to BringIntoView() 
            //  on the text view's associated element, which makes the view invalid...
            if (this.TextView.IsValid && !this.TextView.RendersOwnSelection) 
            {
                // Re-render selection. Need to do this to invalidate and update adorner for this caret element.
                caretElement.UpdateSelection();
            } 

            return null; 
        } 

        // Helper for UpdateCaretState -- identifies caret position from text selection. 
        private static ITextPointer IdentifyCaretPosition(ITextSelection currentTextSelection)
        {
            ITextPointer caretPosition = currentTextSelection.MovingPosition;
 
            if (!currentTextSelection.IsEmpty)
            { 
                // Even when we do not draw the blinking caret, we need this position 
                // for scrolling caret position into view.
 
                // Special case for nonempty selection extended beyond end of line
                if ((caretPosition.LogicalDirection == LogicalDirection.Backward && //
                     caretPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart) || //
                    TextPointerBase.IsAfterLastParagraph(caretPosition)) 
                {
                    // This means that selection has been expanded by ExtendToLineEnd/ExtendToDocumentEnd command. 
                    // TextView in this case cannot give the rect from this position; 
                    // we need to move backward to the end of content
                    caretPosition = caretPosition.CreatePointer(); 
                    caretPosition.MoveToNextInsertionPosition(LogicalDirection.Backward);
                    caretPosition.SetLogicalDirection(LogicalDirection.Forward);
                }
            } 

            // TextView.GetRectangleFromTextPosition returns the end of character rect in case of Backward 
            // logical direction at the start caret position of the docuemtn or paragraph, so we reset the 
            // logical direction with Forward to get the right rect of caret at the start position of
            // document or paragraph. 
            if (caretPosition.LogicalDirection == LogicalDirection.Backward && //
                caretPosition.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart && //
                (caretPosition.GetNextInsertionPosition(LogicalDirection.Backward) == null || //
                 TextPointerBase.IsNextToAnyBreak(caretPosition, LogicalDirection.Backward))) 
            {
                caretPosition = caretPosition.CreatePointer(); 
                caretPosition.SetLogicalDirection(LogicalDirection.Forward); 
            }
 
            return caretPosition;
        }

        // Helper for UpdateCaretState -- calculates caret rectangle from text selection. 
        // regular (non-interim) case of caret positioning
        private static Rect CalculateCaretRectangle(ITextSelection currentTextSelection, ITextPointer caretPosition) 
        { 
            Transform caretTransform;
            Rect caretRectangle = currentTextSelection.TextView.GetRawRectangleFromTextPosition(caretPosition, out caretTransform); 

            if (caretRectangle.IsEmpty)
            {
                // Caret is not at an insertion position, it has no geometry 
                // and will not be displayed.
                return Rect.Empty; 
            } 

            // Convert to local coordiantes. 
            caretRectangle = caretTransform.TransformBounds(caretRectangle);

            // We will use the system defined caret width later.
            caretRectangle.Width = 0; 

            if (currentTextSelection.IsEmpty) 
            { 
                // Calculate caret height - from current font size (ignoring a rect returned by TextView,
                // as it can be a rect of embedded object, which should not affect caret height) 
                double fontSize = (double)currentTextSelection.GetPropertyValue(TextElement.FontSizeProperty);
                FontFamily fontFamily = (FontFamily)currentTextSelection.GetPropertyValue(TextElement.FontFamilyProperty);
                double caretHeight = fontFamily.LineSpacing * fontSize;
                if (caretHeight < caretRectangle.Height) 
                {
                    // Decrease the height of caret to the font height and lower the caret to keep its bottom 
                    // staying on the baseline. 
                    caretRectangle.Y += caretRectangle.Height - caretHeight;
                    caretRectangle.Height = caretHeight; 
                }

                if (!caretTransform.IsIdentity)
                { 
                    Point top = new Point(caretRectangle.X, caretRectangle.Y);
                    Point bottom = new Point(caretRectangle.X, caretRectangle.Y + caretRectangle.Height); 
                    caretTransform.TryTransform(top, out top); 
                    caretTransform.TryTransform(bottom, out bottom);
                    caretRectangle.Y += caretRectangle.Height - Math.Abs(bottom.Y - top.Y); 
                    caretRectangle.Height = Math.Abs(bottom.Y - top.Y);
                }
            }
 
            return caretRectangle;
        } 
 
        // Helper for UpdateCaretState -- handles the korean interim caret.
        private static Rect CalculateInterimCaretRectangle(ITextSelection focusedTextSelection) 
        {
            //

            // Get the current flow direction on the selection of interim. 
            // This is for getting the right size of distance on the interim character
            // whatever the current flow direction is. 
            FlowDirection flowDirection = (FlowDirection)focusedTextSelection.Start.GetValue(FrameworkElement.FlowDirectionProperty); 

            ITextPointer nextCharacterPosition; 
            Rect nextCharacterRectangle;
            Rect caretRectangle;

            if (flowDirection != FlowDirection.RightToLeft) 
            {
                // Flow direction is Left-to-Right 
                // Get the rectangle for both interim selection start position and the next character 
                // position.
                nextCharacterPosition = focusedTextSelection.Start.CreatePointer(LogicalDirection.Forward); 
                caretRectangle = focusedTextSelection.TextView.GetRectangleFromTextPosition(nextCharacterPosition);

                // Get the next character position from the start position of the interim selection
                // on the left to right flow direction. 
                nextCharacterPosition.MoveToNextInsertionPosition(LogicalDirection.Forward);
                nextCharacterPosition.SetLogicalDirection(LogicalDirection.Backward); 
                nextCharacterRectangle = focusedTextSelection.TextView.GetRectangleFromTextPosition(nextCharacterPosition); 
            }
            else 
            {
                // Flow direction is Right-to-Left
                // Get the rectangle for both interim selection end position and the next character
                // position. 
                nextCharacterPosition = focusedTextSelection.End.CreatePointer(LogicalDirection.Backward);
                caretRectangle = focusedTextSelection.TextView.GetRectangleFromTextPosition(nextCharacterPosition); 
 
                // Get the next character position from the end position of the interim selection
                // on the right to left flow direction. 
                nextCharacterPosition.MoveToNextInsertionPosition(LogicalDirection.Backward);
                nextCharacterPosition.SetLogicalDirection(LogicalDirection.Forward);
                nextCharacterRectangle = focusedTextSelection.TextView.GetRectangleFromTextPosition(nextCharacterPosition);
            } 

            // The interim next character position should be great than the current interim position. 
            // Otherwise, we show the caret as the normal state that use the system caret width. 
            // In case of BiDi character, the next character position can be greater than the current position.
            if (!caretRectangle.IsEmpty && !nextCharacterRectangle.IsEmpty && nextCharacterRectangle.Left > caretRectangle.Left) 
            {
                // Get the interim caret width to show the interim block caret.
                caretRectangle.Width = nextCharacterRectangle.Left - caretRectangle.Left;
            } 

            return caretRectangle; 
        } 

        // Helper for UpdateCaretStateWorker -- Calculate the scroll origin position to scroll caret 
        // with the scroll origin position so that we can ensure of displaying caret with the wrapped word.
        //
        // There are four cases of different corrdinate by the flow direction on UiScope and Paragraph.
        // UiScope has two flow direction which is LeftToRightflow directioin and another is RightToLeft. 
        // Paragraph has also two flow direction which is LeftToRightflow directioin and another is RightToLeft.
        // 
        // The below is the example of how horizontal corrdinate and scroll origin value base on the different 
        // four cases. So we have to calculate the scroll to origin position base on the case. Simply we can
        // get the scroll to origin value as zero if UiScope and Paragraph's flow direction is the same. 
        // Otherwise, the scroll to origin value is the extent width value that is the max width.
        //
        // <>
        //  Case 1. 
        //      UiScope FlowDirection:              LTR(LeftToRight)
        //      Paragraph FlowDirection:            LTR(LefTToRight) 
        //          Horizontal origin:              "Left" 
        //          Scroll horizontal origin:       "0"
        //          Wrapping to:                    "Left" 
        //              ABC ......
        //              XYZ|
        //
        //  Case 2. 
        //      UiScope FlowDirection:              LTR(LeftToRight)
        //      Paragraph FlowDirection:            RTL(RightToLeft) 
        //          Horizontal origin:              "Left" 
        //          Scroll horizontal origin:       "Max:Extent Width"
        //          Wrapping to:                    "Right" 
        //              ......ABC
        //                    XYZ|
        //
        //  Case 3. 
        //      UiScope FlowDirection:              RTL(RightToLeft)
        //      Paragraph FlowDirection:            RTL(RightToLeft) 
        //          horizontal origin:              "Right" 
        //          Scroll horizontal origin:       "0"
        //          Wrapping to:                    "Right" 
        //              ......ABC
        //                    XYZ|
        //
        //  Case 4. 
        //      UiScope FlowDirection:              RTL(RightToLeft)
        //      Paragraph FlowDirection:            LTR(LefTToRight) 
        //          horizontal origin:              "Right" 
        //          Scroll horizontal origin:       "Max:Extent Width"
        //          Wrapping to:                    "Left" 
        //              ABC ......
        //              XYZ|
        private static double CalculateScrollToOriginPosition(TextEditor textEditor, ITextPointer caretPosition, double horizontalCaretPosition)
        { 
            double scrollToOriginPosition = double.NaN;
 
            if (textEditor.UiScope is TextBoxBase) 
            {
                double viewportWidth = ((TextBoxBase)textEditor.UiScope).ViewportWidth; 
                double extentWidth = ((TextBoxBase)textEditor.UiScope).ExtentWidth;

                // Calculate the scroll to the origin position position when the horizontal scroll is available
                if (viewportWidth != 0 && extentWidth != 0 && viewportWidth < extentWidth) 
                {
                    bool needScrollToOriginPosition = false; 
 
                    // Check whether we need to calculate the scroll origin position to scroll it with the caret
                    // position. If the caret position is out of the current visual viewport area, the scroll 
                    // to origin positioin will be calculated to scroll into the origin position first that
                    // ensure of displaying the wrapped word.
                    //
                    // Note that horizontalCaretPosition is always relative to the viewport, not the document. 
                    if (horizontalCaretPosition < 0 || horizontalCaretPosition >= viewportWidth)
                    { 
                        needScrollToOriginPosition = true; 
                    }
 
                    if (needScrollToOriginPosition)
                    {
                        // Set the scroll original position as zero
                        scrollToOriginPosition = 0; 

                        // Get the flow direction of uiScope 
                        FlowDirection uiScopeflowDirection = (FlowDirection)textEditor.UiScope.GetValue(FrameworkElement.FlowDirectionProperty); 

                        // Get the flow direction of the current paragraph and compare it with uiScope's flow direction. 
                        Block paragraphOrBlockUIContainer = (caretPosition is TextPointer) ? ((TextPointer)caretPosition).ParagraphOrBlockUIContainer : null;
                        if (paragraphOrBlockUIContainer != null)
                        {
                            FlowDirection pagraphFlowDirection = paragraphOrBlockUIContainer.FlowDirection; 

                            // If the flow direction is different between uiScopoe and paragaph, 
                            // the original scroll position is the extent width value. 
                            if (uiScopeflowDirection != pagraphFlowDirection)
                            { 
                                scrollToOriginPosition = extentWidth;
                            }
                        }
 
                        // Adjust scroll position by current viewport offset
                        scrollToOriginPosition -= ((TextBoxBase)textEditor.UiScope).HorizontalOffset; 
                    } 
                }
            } 

            return scrollToOriginPosition;
        }
 
        private CaretElement EnsureCaret(bool isBlinkEnabled, CaretScrollMethod scrollMethod)
        { 
            TextEditorThreadLocalStore threadLocalStore = TextEditor._ThreadLocalStore; 

            if (_caretElement == null) 
            {
                // Create new caret
                _caretElement = new CaretElement(_textEditor, isBlinkEnabled);
 
                // Check the current input language to draw the BiDi caret in case of BiDi language
                // like as Arabic or Hebrew input language. 
                // 
                if (IsBidiInputLanguage(InputLanguageManager.Current.CurrentInputLanguage))
                { 
                    TextEditor._ThreadLocalStore.Bidi = true;
                }
                else
                { 
                    TextEditor._ThreadLocalStore.Bidi = false;
                } 
            } 
            else
            { 
                _caretElement.SetBlinking(isBlinkEnabled);
            }

            UpdateCaretState(scrollMethod); 

            return _caretElement; 
        } 

        ///  
        /// Walk up the tree from the RenderScope to the UiScope until we find an
        /// AdornerDecorator or ScrollContentPresenter.
        /// 
        /// true if one is found, false otherwise 
        private bool VerifyAdornerLayerExists()
        { 
            DependencyObject element = TextView.RenderScope; 
            while (element != _textEditor.UiScope && element != null)
            { 
                if (element is AdornerDecorator || element is System.Windows.Controls.ScrollContentPresenter)
                {
                    return true;
                } 
                element = VisualTreeHelper.GetParent(element);
            } 
            return false; 
        }
 
        //......................................................
        //
        //  BIDI Support
        // 
        //......................................................
 
        ///  
        /// Check the input language of cultureInfo whether it is the bi-directional language or not.
        ///  
        /// 
        /// 
        /// Return true if the passed cultureInfo is the bi-directional language like as Arabic or Hebrew.
        /// Otherwise, return false. 
        /// 
        ///  
        /// Critical - calls unmanaged code to check the font signature 
        /// TreatAsSafe - data exposed (user using bidi) is safe to release
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private static bool IsBidiInputLanguage(CultureInfo cultureInfo)
        {
            bool bidiInput; 
            string fontSignature;
 
            bidiInput = false; 

            fontSignature = new String(new Char[FONTSIGNATURE_SIZE]); 

            // Get the font signature to know the current LCID is BiDi(Arabic, Hebrew etc.) or not.
            if (UnsafeNativeMethods.GetLocaleInfoW(cultureInfo.LCID, NativeMethods.LOCALE_FONTSIGNATURE, fontSignature, FONTSIGNATURE_SIZE) != 0)
            { 
                // Compare fontSignature[7] with 0x0800 to detect BiDi language.
                if ((fontSignature[FONTSIGNATURE_BIDI_INDEX] & FONTSIGNATURE_BIDI) != 0) 
                { 
                    bidiInput = true;
                } 
            }

            return bidiInput;
        } 

        //...................................................... 
        // 
        //  Table Selection
        // 
        //......................................................

        private static TableCell FindCellAtColumnIndex(TableCellCollection cells, int columnIndex)
        { 
            for (int cellIndex = 0; cellIndex < cells.Count; cellIndex++)
            { 
                TableCell cell; 
                int startColumnIndex;
                int endColumnIndex; 

                cell = cells[cellIndex];
                startColumnIndex = cell.ColumnIndex;
                endColumnIndex = startColumnIndex + cell.ColumnSpan - 1; 

                if (startColumnIndex <= columnIndex && columnIndex <= endColumnIndex) 
                { 
                    return cell;
                } 
            }

            return null;
        } 

        ///  
        /// Determines if the given element has any ancestor. 
        /// 
        ///  
        /// 
        private static bool IsRootElement(DependencyObject element)
        {
            return GetParentElement(element) == null; 
        }
 
        ///  
        /// Workaround to approximate whether or not our window is active.
        ///  
        /// 
        private bool IsFocusWithinRoot()
        {
            DependencyObject element = this.UiScope; 
            DependencyObject parent = this.UiScope;
 
            while (parent != null) 
            {
                element = parent; 
                parent = GetParentElement(element);
            }

            if (element is UIElement && ((UIElement)element).IsKeyboardFocusWithin) 
            {
                return true; 
            } 

            return false; 
        }

        /// 
        /// Helper method to determine an element's parent, using the umpteen methods of 
        /// parenting.
        ///  
        ///  
        private static DependencyObject GetParentElement(DependencyObject element)
        { 
            DependencyObject parent;

            if (element is FrameworkElement || element is FrameworkContentElement)
            { 
                parent = LogicalTreeHelper.GetParent(element);
                if (parent == null && element is FrameworkElement) 
                { 
                    parent = ((FrameworkElement)element).TemplatedParent;
                    if (parent == null && element is Visual) 
                    {
                        parent = VisualTreeHelper.GetParent(element);
                    }
                } 
            }
            else if (element is Visual) 
            { 
                parent = VisualTreeHelper.GetParent(element);
            } 
            else
            {
                parent = null;
            } 

            return parent; 
        } 

        // Removes the caret from the visual tree. 
        private void DetachCaretFromVisualTree()
        {
            if (_caretElement != null)
            { 
                _caretElement.DetachFromView();
                _caretElement = null; 
            } 
        }
 
        #endregion Private methods

        //------------------------------------------------------
        // 
        //  Private Properties
        // 
        //----------------------------------------------------- 

        #region Private Properties 

        TextEditor ITextSelection.TextEditor
        {
            get 
            {
                return _textEditor; 
            } 
        }
 
        ITextView ITextSelection.TextView
        {
            get
            { 
                return _textEditor.TextView;
            } 
        } 

        private ITextView TextView 
        {
            get
            {
                return ((ITextSelection)this).TextView; 
            }
        } 
 
        private TextStore TextStore
        { 
            get
            {
                return _textEditor.TextStore;
            } 
        }
 
        private ImmComposition ImmComposition 
        {
            get 
            {
                return _textEditor.ImmComposition;
            }
        } 

        // UiScope associated with this TextSelection's TextEditor. 
        private FrameworkElement UiScope 
        {
            get 
            {
                return _textEditor.UiScope;
            }
        } 

        // Position from which to spring load property values. 
        private ITextPointer PropertyPosition 
        {
            get 
            {
                ITextSelection This = this;
                ITextPointer position = null;
 
                if (!This.IsEmpty)
                { 
                    position = TextPointerBase.GetFollowingNonMergeableInlineContentStart(This.Start); 
                }
 
                if (position == null)
                {
                    position = This.Start;
                } 

                position.Freeze(); 
                return position; 
            }
        } 

        #endregion Private Properties

        //----------------------------------------------------- 
        //
        //  Private Types 
        // 
        //-----------------------------------------------------
 
        #region Private Types

        // The four possible positions for the selection moving position.
        private enum MovingEdge { Start, StartInner, EndInner, End, None }; 

        #endregion Private Types 
 
        //------------------------------------------------------
        // 
        //  Private Fields
        //
        //-----------------------------------------------------
 
        #region Private Fields
 
        // Selected Content 
        // ----------------
 
        // TextEditor that owns this selection.
        private TextEditor _textEditor;

        // Container for highlights on selected text. 
        private TextSelectionHighlightLayer _highlightLayer;
 
        // Springload Formatting 
        // ---------------------
 
        // Dependency object representing a set of springloaded formatting properties
        private DependencyObject _springloadFormatting;

        // Selection autoexpansion for unit boundaries 
        // -------------------------------------------
 
        // A position where the last selection gesture has been initiated. 
        // Selection gestures are: mouseDown-Move-...-Move-Up, Shift+ArrowDown-...-Down-Up.
        // Note that Shift+MouseDown-Move-...-Move-Up is a gesture continuation, 
        // it does not change _anchorPosition.
        // Actual selection always contains this position but may be extended
        // to a wider range - to encompass whole units such as words,
        // hyperlinks, sentences, paragraphs, table cells etc. 
        //
 
 

        private ITextPointer _anchorPosition; 

        // A position to which user input is applied.
        // It may coinside with Start or End, but it may be also in some other
        // corner of rectangular table range. 
        private MovingEdge _movingPositionEdge;
 
        // LogicalDirection for the moving position. 
        // If the selection is empty, this value is ignored and
        // the direction matches this.Start.  Otherwise we respect 
        // the value, which typically points inwards towards the
        // content but may point outward to include an empty line.
        private LogicalDirection _movingPositionDirection;
 
        // Text position used on previous step of mouse dragging selection
        // It is used in selection dragging heuristic to identify a situation when 
        // dragging end returned back to selected area which means that 
        // autoword expansion must be temporary stopped - until a new word
        // boundary is crossed. See also _reenterPosition. 
        private ITextPointer _previousCursorPosition;

        // Text position where dragged mouse re-entered a selection. This word should not be autoexapnded
        private ITextPointer _reenterPosition; 

        // Flag indicating that initial word boundary has been crossed at least once doring selection expansion 
        private bool _anchorWordRangeHasBeenCrossedOnce; 

        // Flag allows autoword expansion for anchor end of selection 
        private bool _allowWordExpansionOnAnchorEnd;

        // Font signature size as 16
        private const int FONTSIGNATURE_SIZE = 16; 

        // BIDI font signature index from GetLocaleInfo. 
        private const int FONTSIGNATURE_BIDI_INDEX = 7; 

        // BIDI font signature value 
        private const int FONTSIGNATURE_BIDI = 0x0800;

        // Signals how the caret should be scrolled into view on the next
        // caret update. 
        private CaretScrollMethod _caretScrollMethod;
 
        // If true, signals that the next caret 
        // scroll-into-view should include hueristics to include following
        // text. 
        private bool _pendingCaretNavigation;

        // Caret associated with this selection.
        private CaretElement _caretElement; 

        #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