ImmComposition.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 / ImmComposition.cs / 5 / ImmComposition.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: 
//  This class handles IMM32 IME's composition string and support level 3 input to TextBox and RichTextBox.
// 
// History:
//  11/09/2004 : [....] - created
//
//--------------------------------------------------------------------------- 

using System; 
using System.Runtime.InteropServices; 
using System.Threading;
using System.Collections; 
using System.Diagnostics;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Documents; 
using System.Windows.Interop;
using System.Windows.Threading; 
using System.Security; 
using System.Security.Permissions;
using System.Text; 
using MS.Win32;
using MS.Internal.Documents;
using MS.Internal.PresentationFramework;
using MS.Internal; 

// Enable presharp pragma warning suppress directives. 
#pragma warning disable 1634, 1691 

namespace System.Windows.Documents 
{
    //-----------------------------------------------------
    //
    //  ImmComposition class 
    //
    //----------------------------------------------------- 
 
    //
    // This class handles IMM32 IME's composition string and 
    // support level 3 input to TextBox and RichTextBox.
    //
    internal class ImmComposition
    { 
        //------------------------------------------------------
        // 
        //  Constructors 
        //
        //----------------------------------------------------- 

        #region Constructors

        // 
        // Creates a new ImmComposition instance.
        // 
        ///  
        /// Critical - This class exists purely to prevent the security exceptions from
        /// percolating 
        /// TreatAsSafe: Ok to expose
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        static ImmComposition() 
        {
        } 
 
        //
        // Creates a new ImmComposition instance. 
        //
        /// 
        /// Critical - calls critical code (UpdateSource)
        ///  
        [SecurityCritical]
        internal ImmComposition(HwndSource source) 
        { 
            UpdateSource(null, source);
        } 

        #endregion Constructors

        //------------------------------------------------------ 
        //
        //  Internal Method 
        // 
        //------------------------------------------------------
 
        //
        // Create an instance of ImmComposition per source window.
        //
        ///  
        /// Critical - gets HwndSource (protected), then creates a new
        ///            composition based on this. 
        ///  
        [SecurityCritical]
        internal static ImmComposition GetImmComposition(FrameworkElement scope) 
        {
            HwndSource source = PresentationSource.CriticalFromVisual(scope) as HwndSource;

            ImmComposition immComposition = null; 

            if (source != null) 
            { 
                lock (_list)
                { 
                    immComposition = (ImmComposition)_list[source];

                    if (immComposition == null)
                    { 
                        immComposition = new ImmComposition(source);
                        _list[source] = immComposition; 
                    } 
                }
            } 

            return immComposition;
        }
 
        //
        // This is called when TextEditor is detached. 
        // We need to remove event handlers. 
        //
        ///  
        ///   Critical: This code removes the handler for OnSourceChanged which is critical
        ///   TreatAsSafe:Removing the handler is a safe operation
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        internal void OnDetach()
        { 
            if (_editor != null) 
            {
                PresentationSource.RemoveSourceChangedHandler(UiScope, new SourceChangedEventHandler(OnSourceChanged)); 
                _editor.TextContainer.Change -= new TextContainerChangeEventHandler(OnTextContainerChange);
            }

            _editor = null; 
        }
 
        // 
        // Callback from TextEditor when it gets focus.
        // 
        /// 
        ///    Critical: This code calls into PresentationSource to remove and add source changed handlers
        ///    TreatAsSafe: Calling this is safe. Since this does not expose the presentation source. Also the
        ///                 handler that is attached is private and critical 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        internal void OnGotFocus(TextEditor editor) 
        {
            if (editor == _editor) 
            {
                // If an event listener does a reentrant SetFocus, we can get
                // here without a matching OnLostFocus.  Early out so
                // that we don't attach too many handlers. 
                return;
            } 
 
            // remove source changed handler for previous editor.
            if (_editor != null) 
            {
                PresentationSource.RemoveSourceChangedHandler(UiScope, new SourceChangedEventHandler(OnSourceChanged));
                _editor.TextContainer.Change -= new TextContainerChangeEventHandler(OnTextContainerChange);
            } 

            // Update the current focus TextEditor, RenderScope and UiScope. 
            _editor = editor; 

            // we need to track the source change. 
            PresentationSource.AddSourceChangedHandler(UiScope, new SourceChangedEventHandler(OnSourceChanged));

            _editor.TextContainer.Change += new TextContainerChangeEventHandler(OnTextContainerChange);
 
            // Update the current composition window position.
            UpdateNearCaretCompositionWindow(); 
        } 

        // 
        // Callback from TextEditor when it lost focus.
        //
        internal void OnLostFocus()
        { 
            if (_editor == null)
                return; 
 
            _losingFocus = true;
            try 
            {
                // complete the composition string when it lost focus.
                CompleteComposition();
            } 
            finally
            { 
                _losingFocus = false; 
            }
        } 

        //
        // Callback from TextEditor when the layout is updated
        // 
        internal void OnLayoutUpdated()
        { 
            if (_updateCompWndPosAtNextLayoutUpdate && IsReadingWindowIme()) 
            {
                UpdateNearCaretCompositionWindow(); 
            }
            _updateCompWndPosAtNextLayoutUpdate = false;
        }
 
        //
        // complete the composition string by calling ImmNotifyIME. 
        // 
        /// 
        /// Critical - elevates to access protected resource (hwnd) 
        /// TreatAsSafe - forces the composition to complete, which at worst
        ///               causes someone's current input to commit. No additional
        ///               spoofing or information disclosure could occur.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal void CompleteComposition() 
        { 
            UnregisterMouseListeners();
 
            if (_source == null)
            {
                // Do nothing if HwndSource is already gone(disposed) or disconnected.
                return; 
            }
 
            _compositionModifiedByApp = true; 

            IntPtr hwnd = IntPtr.Zero; 

            new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert
            try
            { 
                hwnd = ((IWin32Window)_source).Handle;
            } 
            finally 
            {
                CodeAccessPermission.RevertAssert(); 
            }

            IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
            if (himc != IntPtr.Zero) 
            {
                UnsafeNativeMethods.ImmNotifyIME(new HandleRef(this, himc), NativeMethods.NI_COMPOSITIONSTR, NativeMethods.CPS_COMPLETE, 0); 
 
                UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc));
            } 

            if (_compositionAdorner != null)
            {
                _compositionAdorner.Uninitialize(); 
                _compositionAdorner = null;
            } 
 
            _startComposition = null;
            _endComposition = null; 
        }

        // Called as the selection changes.
        // We can't modify document state here in any way. 
        internal void OnSelectionChange()
        { 
            _compositionModifiedByApp = true; 
        }
 
        // Callback for TextSelection.Changed event.
        internal void OnSelectionChanged()
        {
            if (!this.IsInKeyboardFocus) 
            {
                return; 
            } 

            // Update the current composition window position. 
            UpdateNearCaretCompositionWindow();
        }

        //----------------------------------------------------- 
        //
        //  Internal Properties 
        // 
        //------------------------------------------------------
 
        //
        // Returns true if we're in the middle of an ongoing composition.
        //
        internal bool IsComposition 
        {
            get 
            { 
                return _startComposition != null;
            } 
        }

        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 

        //----------------------------------------------------- 
        //
        // SourceChanged callback
        //
        ///  
        /// Critical - calls critical code - NewSource, OldSource and UpdateSource.
        ///  
        [SecurityCritical] 
        private void OnSourceChanged(object sender, SourceChangedEventArgs e)
        { 
            HwndSource newSource = null;
            HwndSource oldSource = null;

            new UIPermission(PermissionState.Unrestricted).Assert(); // BlessedAssert 
            try
            { 
                newSource = e.NewSource as HwndSource; 
                oldSource = e.OldSource as HwndSource;
            } 
            finally
            {
                UIPermission.RevertAssert();
            } 

            UpdateSource(oldSource, newSource); 
 
            // Clean up the old source changed event handler that was connected with UiScope.
            if (oldSource != null && UiScope != null) 
            {
                // Remove the source changed event handler here.
                // Ohterwise, we'll get the leak of the SourceChangedEventHandler.
                // New source changed event handler will be added by getting OnGotFocus on new UiScope. 
                PresentationSource.RemoveSourceChangedHandler(UiScope, new SourceChangedEventHandler(OnSourceChanged));
            } 
        } 

        // 
        // Update _list and _source with new source.
        //
        /// 
        /// Critical - Calls critical code (add/remove hook) 
        /// 
        [SecurityCritical] 
        private void UpdateSource(HwndSource oldSource, HwndSource newSource) 
        {
 
            if (_source != null)
            {
                Debug.Assert((oldSource == null) || (oldSource == _source));
 
                new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert
                try 
                { 
                    _source.RemoveHook(new HwndSourceHook(ImmCompositionFilterMessage));
                } 
                finally
                {
                    UIPermission.RevertAssert();
                } 
                _source.Disposed -= new EventHandler(OnHwndDisposed);
 
                // Remove HwndSource from the list. 
                _list.Remove(_source);
                _source = null; 
            }

            if (newSource != null)
            { 
                _list[newSource] = this;
                _source = newSource; 
                new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert 
                try
                { 
                    _source.AddHook(new HwndSourceHook(ImmCompositionFilterMessage));
                }
                finally
                { 
                    UIPermission.RevertAssert();
                } 
                _source.Disposed += new EventHandler(OnHwndDisposed); 
            }
 
            // _source should always be a newSource.
            Debug.Assert(newSource == _source);
        }
 
        //
        // Window Hook to track WM_IME_ messages. 
        // 
        /// 
        /// Critical - access raw Win32 messages, raw input, etc. and can be used to spoof input 
        /// 
        [SecurityCritical]
        private IntPtr ImmCompositionFilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        { 
            IntPtr lret = IntPtr.Zero ;
            switch (msg) 
            { 
                case NativeMethods.WM_IME_CHAR:
                    OnWmImeChar(wParam, ref handled); 
                    break;

                case NativeMethods.WM_IME_NOTIFY:
                    // we don't have to update handled. 
                    OnWmImeNotify(hwnd, wParam);
                    break; 
 
                case NativeMethods.WM_IME_STARTCOMPOSITION:
                case NativeMethods.WM_IME_ENDCOMPOSITION: 
                    if (IsInKeyboardFocus && !IsReadOnly)
                    {
                        // Do Level 2 for legacy Chinese IMM32 IMEs.
                        if (!IsReadingWindowIme()) 
                        {
                            handled = true; 
                        } 
                    }
 
                    break;

                case NativeMethods.WM_IME_COMPOSITION:
                    OnWmImeComposition(hwnd, lParam, ref handled); 
                    break;
 
                case NativeMethods.WM_IME_REQUEST: 
                    lret = OnWmImeRequest(wParam, lParam, ref handled);
                    break; 

                case NativeMethods.WM_INPUTLANGCHANGE:
                    // Set the composition window position (reading window position) for
                    // legacy Chinese IMM32 IMEs. 
                    if (IsReadingWindowIme())
                    { 
                        UpdateNearCaretCompositionWindow(); 
                    }
                    break; 
            }

            return lret;
        } 

        // 
        // WM_IME_COMPOSITION handler 
        //
        ///  
        /// Critical - This can be used to spoof input and it takes IntPtr from untrusted sources
        /// 
        [SecurityCritical]
        private void OnWmImeComposition(IntPtr hwnd, IntPtr lParam, ref bool handled) 
        {
            IntPtr himc; 
            int size; 
            int cursorPos = 0;
            int deltaStart = 0; 
            char[] result = null;
            char[] composition = null;
            int[] clauseInfo = null;
            byte[] attributes = null; 

            if (IsReadingWindowIme()) 
            { 
                // Don't handle WM_IME_COMPOSITION for Chinese Legacy IMEs.
                return; 
            }

            if (!IsInKeyboardFocus && !_losingFocus)
            { 
                // Don't handle WM_IME_COMPOSITION if we don't have a focus.
                return; 
            } 

            if (IsReadOnly) 
            {
                // Don't handle WM_IME_COMPOSITION if it is readonly.
                return;
            } 

 
            himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); 
            if (himc == IntPtr.Zero)
            { 
                // we don't do anything with NULL-HIMC.
                return;
            }
 
            //
            // Get the result string from hIMC. 
            // 
            if (((int)lParam & NativeMethods.GCS_RESULTSTR) != 0)
            { 
                size = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_RESULTSTR, IntPtr.Zero, 0);
                if (size > 0)
                {
                    result = new char[size / Marshal.SizeOf(typeof(short))]; 

                    // 3rd param is out and contains actual result of this call. 
                    // suppress Presharp 6031. 
#pragma warning suppress 6031
                    UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_RESULTSTR, result, size); 
                }
            }

            // 
            // Get the composition string from hIMC.
            // 
            if (((int)lParam & NativeMethods.GCS_COMPSTR) != 0) 
            {
                size = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPSTR, IntPtr.Zero, 0); 
                if (size > 0)
                {
                    composition = new char[size / Marshal.SizeOf(typeof(short))];
                    // 3rd param is out and contains actual result of this call. 
                    // suppress Presharp 6031.
#pragma warning suppress 6031 
                    UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPSTR, composition,  size); 

                    // 
                    // Get the caret position from hIMC.
                    //
                    if (((int)lParam & NativeMethods.GCS_CURSORPOS) != 0)
                    { 
                        cursorPos = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_CURSORPOS, IntPtr.Zero, 0);
                    } 
 
                    //
                    // Get the delta start position from hIMC. 
                    //
                    if (((int)lParam & NativeMethods.GCS_DELTASTART) != 0)
                    {
                        deltaStart = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_DELTASTART, IntPtr.Zero, 0); 
                    }
 
                    // 
                    // Get the clause information from hIMC.
                    // 
                    if (((int)lParam & NativeMethods.GCS_COMPCLAUSE) != 0)
                    {
                        size = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPCLAUSE, IntPtr.Zero, 0);
                        if (size > 0) 
                        {
                            clauseInfo = new int[size / Marshal.SizeOf(typeof(int))]; 
                            // 3rd param is out and contains actual result of this call. 
                            // suppress Presharp 6031.
#pragma warning suppress 6031 
                            UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPCLAUSE, clauseInfo,  size);
                        }
                    }
 
                    //
                    // Get the attribute information from hIMC. 
                    // 
                    if (((int)lParam & NativeMethods.GCS_COMPATTR) != 0)
                    { 
                        size = UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPATTR, IntPtr.Zero, 0);
                        if (size > 0)
                        {
                            attributes = new byte[size / Marshal.SizeOf(typeof(byte))]; 
                            // 3rd param is out and contains actual result of this call.
                            // suppress Presharp 6031. 
#pragma warning suppress 6031 
                            UnsafeNativeMethods.ImmGetCompositionString(new HandleRef(this, himc), NativeMethods.GCS_COMPATTR, attributes,  size);
                        } 
                    }
                }
            }
 
            UpdateCompositionString(result, composition, cursorPos, deltaStart, clauseInfo, attributes);
 
            UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); 
            handled = true;
        } 

        //
        // WM_IME_CHAR handler
        // 
        /// 
        /// Critical - This can be used to spoof input and it takes IntPtr from untrusted sources 
        ///  
        [SecurityCritical]
        private void OnWmImeChar(IntPtr wParam, ref bool handled) 
        {
            if (!IsInKeyboardFocus && !_losingFocus)
            {
                // Don't handle WM_IME_CAHR if we don't have a focus. 
                return;
            } 
 
            if (IsReadOnly)
            { 
                // Don't handle WM_IME_CAHR if it is readonly.
                return;
            }
 
            if (_handlingImeMessage)
            { 
                // We will be called reentrantly while completing compositions 
                // in response to application listeners.  In that case, don't
                // propegate events to listeners. 
                return;
            }

            _handlingImeMessage = true; 
            try
            { 
                int resultLength; 
                string compositionString = BuildCompositionString(null, new char[] { (char)wParam }, out resultLength);
 
                if (compositionString == null)
                {
                    CompleteComposition();
                } 
                else
                { 
                    FrameworkTextComposition composition = TextStore.CreateComposition(_editor, this); 
                    _compositionModifiedByApp = false;
                    _caretOffset = 1; 

                    //
                    // Raise TextInputStart.
                    // 
                    bool handledbyApp = RaiseTextInputStartEvent(composition, resultLength, compositionString);
 
                    if (handledbyApp) 
                    {
                        CompleteComposition(); 
                    }
                    else
                    {
                        // 
                        // Raise TextInput.
                        // 
                        bool handledByApp = RaiseTextInputEvent(composition, compositionString); 

                        if (handledByApp) 
                        {
                            CompleteComposition();
                            goto Exit;
                        } 
                    }
                } 
            } 
            finally
            { 
                _handlingImeMessage = false;
            }

            // the string has been finalized. Update the reading window position for 
            // legacy Chinese IMEs.
            if (IsReadingWindowIme()) 
            { 
                UpdateNearCaretCompositionWindow();
            } 

Exit:
            handled = true;
        } 

        // 
        // WM_IME_NOTIFY handler 
        //
        ///  
        /// Critical - accepts raw Win32 messages, calls unmanaged code, deals
        ///            with unmanaged data structures
        /// 
        [SecurityCritical] 
        private void OnWmImeNotify(IntPtr hwnd, IntPtr wParam)
        { 
            int convmode; 
            int sentence;
            IntPtr himc; 

            // we don't have to do anything if _editor is null.
            if (!IsInKeyboardFocus)
            { 
                return;
            } 
 
            switch ( (int) wParam)
            { 
                case NativeMethods.IMN_OPENCANDIDATE:
                    himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
                    if (himc != IntPtr.Zero)
                    { 
                        NativeMethods.CANDIDATEFORM candform = new NativeMethods.CANDIDATEFORM();
                        // 
                        // At IMN_OPENCANDIDATE, we need to set the candidate window location to hIMC. 
                        //
                        if (IsReadingWindowIme()) 
                        {
                             // Level 2 for Chinese legacy IMEs.
                             // We have already set the composition form. The candidate window will follow it.
                             candform.dwIndex = 0; 
                             candform.dwStyle = NativeMethods.CFS_DEFAULT;
                             candform.rcArea.left   = 0; 
                             candform.rcArea.right  = 0; 
                             candform.rcArea.top    = 0;
                             candform.rcArea.bottom = 0; 
                             candform.ptCurrentPos  = new NativeMethods.POINT(0, 0);
                        }
                        else
                        { 
                            ITextView view;
                            ITextPointer startNavigator; 
                            ITextPointer endNavigator; 
                            ITextPointer caretNavigator;
                            GeneralTransform transform; 
                            Point milPointTopLeft;
                            Point milPointBottomRight;
                            Point milPointCaret;
                            Rect rectStart; 
                            Rect rectEnd;
                            Rect rectCaret; 
                            CompositionTarget compositionTarget; 

                            compositionTarget = _source.CompositionTarget; 

                            if (_startComposition != null)
                            {
                                startNavigator = _startComposition.CreatePointer(); 
                            }
                            else 
                            { 
                                startNavigator = _editor.Selection.Start.CreatePointer();
                            } 

                            if (_endComposition != null)
                            {
                                endNavigator = _endComposition.CreatePointer(); 
                            }
                            else 
                            { 
                                endNavigator = _editor.Selection.End.CreatePointer();
                            } 

                            if (_startComposition != null)
                            {
                                caretNavigator = _caretOffset > 0 ? _startComposition.CreatePointer(_caretOffset, LogicalDirection.Forward) : _endComposition; 
                            }
                            else 
                            { 
                                caretNavigator = _editor.Selection.End.CreatePointer();
                            } 

                            ITextPointer startPosition = startNavigator.CreatePointer(LogicalDirection.Forward);
                            ITextPointer endPosition = endNavigator.CreatePointer(LogicalDirection.Backward);
                            ITextPointer caretPosition = caretNavigator.CreatePointer(LogicalDirection.Forward); 

                            // We need to update the layout before getting rect. It could be dirty. 
                            if (!startPosition.ValidateLayout() || 
                                !endPosition.ValidateLayout() ||
                                !caretPosition.ValidateLayout()) 
                            {
                                return;
                            }
 
                            view = TextEditor.GetTextView(RenderScope);
 
                            rectStart = view.GetRectangleFromTextPosition(startPosition); 
                            rectEnd = view.GetRectangleFromTextPosition(endPosition);
                            rectCaret = view.GetRectangleFromTextPosition(caretPosition); 

                            // Take the "extended" union of the first and last char's bounding box.
                            milPointTopLeft = new Point(Math.Min(rectStart.Left, rectEnd.Left), Math.Min(rectStart.Top, rectEnd.Top));
                            milPointBottomRight = new Point(Math.Max(rectStart.Left, rectEnd.Left), Math.Max(rectStart.Bottom, rectEnd.Bottom)); 
                            milPointCaret = new Point(rectCaret.Left, rectCaret.Bottom);
 
                            // Transform to root visual coordinates. 
                            transform = RenderScope.TransformToAncestor(compositionTarget.RootVisual);
                            transform.TryTransform(milPointTopLeft, out milPointTopLeft); 
                            transform.TryTransform(milPointBottomRight, out milPointBottomRight);
                            transform.TryTransform(milPointCaret, out milPointCaret);

                            // Transform to device units. 
                            milPointTopLeft = compositionTarget.TransformToDevice.Transform(milPointTopLeft);
                            milPointBottomRight = compositionTarget.TransformToDevice.Transform(milPointBottomRight); 
                            milPointCaret = compositionTarget.TransformToDevice.Transform(milPointCaret); 

                            // Build CANDIDATEFORM. CANDIDATEFORM is window coodidate. 
                            candform.dwIndex = 0;
                            candform.dwStyle = NativeMethods.CFS_EXCLUDE;
                            candform.rcArea.left   = ConvertToInt32(milPointTopLeft.X);
                            candform.rcArea.right  = ConvertToInt32(milPointBottomRight.X); 
                            candform.rcArea.top    = ConvertToInt32(milPointTopLeft.Y);
                            candform.rcArea.bottom = ConvertToInt32(milPointBottomRight.Y); 
                            candform.ptCurrentPos  = new NativeMethods.POINT(ConvertToInt32(milPointCaret.X), ConvertToInt32(milPointCaret.Y)); 

                        } 
                        // Call IMM32 to set new candidate position to hIMC.
                        // ImmSetCandidateWindow fails when
                        //  - candform.dwIndex is invalid (over 4).
                        //  - himc belongs to other threads. 
                        //  - fail to lock IMC.
                        // Those cases are ignorable for us. 
                        // In addition, it does not set win32 last error and we have no clue to handle error. 
#pragma warning suppress 6031
                        UnsafeNativeMethods.ImmSetCandidateWindow(new HandleRef(this, himc), ref candform); 
                        UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc));
                    }

                    // We want to pass this message to DefWindowProc. 
                    // We don't update "handled".
                    break; 
 

                case NativeMethods.IMN_SETCONVERSIONMODE: 
                    // The Conversion Mode has been changed by IMM32 API. We need to update InputMethod.ImeConversionMode property.
                    ImeConversionModeValues imeConversionMode = 0;
                    convmode = 0;
                    sentence = 0; 
                    himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
                    if (himc != IntPtr.Zero) 
                    { 
                        UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence);
                        UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); 

                        // IME_CMODE_ALPHANUMERIC is 0.
                        if ((convmode & (NativeMethods.IME_CMODE_NATIVE | NativeMethods.IME_CMODE_KATAKANA)) == 0)
                            imeConversionMode |= ImeConversionModeValues.Alphanumeric; 
                        if ((convmode & NativeMethods.IME_CMODE_NATIVE) != 0)
                            imeConversionMode |= ImeConversionModeValues.Native; 
                        if ((convmode & NativeMethods.IME_CMODE_KATAKANA) != 0) 
                            imeConversionMode |= ImeConversionModeValues.Katakana;
                        if ((convmode & NativeMethods.IME_CMODE_FULLSHAPE) != 0) 
                            imeConversionMode |= ImeConversionModeValues.FullShape;
                        if ((convmode & NativeMethods.IME_CMODE_ROMAN) != 0)
                            imeConversionMode |= ImeConversionModeValues.Roman;
                        if ((convmode & NativeMethods.IME_CMODE_CHARCODE) != 0) 
                            imeConversionMode |= ImeConversionModeValues.CharCode;
                        if ((convmode & NativeMethods.IME_CMODE_NOCONVERSION) != 0) 
                            imeConversionMode |= ImeConversionModeValues.NoConversion; 
                        if ((convmode & NativeMethods.IME_CMODE_EUDC) != 0)
                            imeConversionMode |= ImeConversionModeValues.Eudc; 
                        if ((convmode & NativeMethods.IME_CMODE_SYMBOL) != 0)
                            imeConversionMode |= ImeConversionModeValues.Symbol;
                        if ((convmode & NativeMethods.IME_CMODE_FIXED) != 0)
                            imeConversionMode |= ImeConversionModeValues.Fixed; 
                        InputMethod.Current.ImeConversionMode = imeConversionMode;
                    } 
                    break; 

                case NativeMethods.IMN_SETSENTENCEMODE: 
                    // The Sentence Mode has been changed by IMM32 API. We need to update InputMethod.ImeSentenceMode property.
                    ImeSentenceModeValues imeSentenceMode = 0;
                    convmode = 0;
                    sentence = 0; 
                    himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
                    if (himc != IntPtr.Zero) 
                    { 
                        UnsafeNativeMethods.ImmGetConversionStatus(new HandleRef(this, himc), ref convmode, ref sentence);
                        UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc)); 

                        // TF_SENTENCEMODE_ALPHANUMERIC is 0.
                        if (sentence == NativeMethods.IME_SMODE_NONE)
                        { 
                            imeSentenceMode = ImeSentenceModeValues.None;
                        } 
                        else 
                        {
                            if ((sentence & NativeMethods.IME_SMODE_PLAURALCLAUSE) != 0) 
                                imeSentenceMode |= ImeSentenceModeValues.PluralClause;
                            if ((sentence & NativeMethods.IME_SMODE_SINGLECONVERT) != 0)
                                imeSentenceMode |= ImeSentenceModeValues.SingleConversion;
                            if ((sentence & NativeMethods.IME_SMODE_AUTOMATIC) != 0) 
                                imeSentenceMode |= ImeSentenceModeValues.Automatic;
                            if ((sentence & NativeMethods.IME_SMODE_PHRASEPREDICT) != 0) 
                                imeSentenceMode |= ImeSentenceModeValues.PhrasePrediction; 
                            if ((sentence & NativeMethods.IME_SMODE_CONVERSATION) != 0)
                                imeSentenceMode |= ImeSentenceModeValues.Conversation; 
                        }
                        InputMethod.Current.ImeSentenceMode = imeSentenceMode;
                    }
                    break; 

                case NativeMethods.IMN_SETOPENSTATUS: 
                    // The open status has been changed by IMM32 API. We need to update InputMethod.ImeState property. 
                    himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd));
                    if (himc != IntPtr.Zero) 
                    {
                        bool fOpen = UnsafeNativeMethods.ImmGetOpenStatus(new HandleRef(this, himc));
                        UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc));
 
                        InputMethod.Current.ImeState = fOpen ? InputMethodState.On : InputMethodState.Off;
                    } 
                    break; 
            }
        } 

        //
        // Use Level 2 for Chinese IME
        // 
        /// 
        /// Critical - elevates to access protected resources (hwnd) 
        /// TreatAsSafe - positions the composition window, which is safe to do 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void UpdateNearCaretCompositionWindow()
        {
            ITextView view;
            Rect rectUi; 
            GeneralTransform transform;
            Point milPointTopLeft; 
            Point milPointBottomRight; 
            Point milPointCaret;
            Rect rectCaret; 
            CompositionTarget compositionTarget;
            IntPtr hwnd;

            if (!IsInKeyboardFocus) 
            {
                return; 
            } 

            if (_source == null) 
            {
                return;
            }
 
            // get hwnd from _source.
            new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert 
            try 
            {
                hwnd = ((IWin32Window)_source).Handle; 
            }
            finally
            {
                CodeAccessPermission.RevertAssert(); 
            }
 
            rectUi = UiScope.VisualContentBounds; 
            view = _editor.TextView;
 
            //
            //

 

 
 
            // During incremental layout update, the region of the view covered by
            // the selection may not be ready yet. 
            if (!_editor.Selection.End.HasValidLayout)
            {
                _updateCompWndPosAtNextLayoutUpdate = true;
                return; 
            }
 
            compositionTarget = _source.CompositionTarget; 

            // HwndSource.CompositionTarget may return null if the target hwnd is being destroyed and disposed. 
            if (compositionTarget == null)
            {
                return;
            } 

            // If the mouse click happens before rendering, the seleciton move notification is generated. 
            // However the visual tree is not completely connected yet. We need to check it. 
            if (!compositionTarget.RootVisual.IsAncestorOf(RenderScope))
            { 
                return;
            }

            IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); 
            if (himc != IntPtr.Zero)
            { 
                rectCaret = view.GetRectangleFromTextPosition(_editor.Selection.End.CreatePointer(LogicalDirection.Backward)); 

                // Take the points of the renderScope. 
                milPointTopLeft = new Point(rectUi.Left, rectUi.Top);
                milPointBottomRight = new Point(rectUi.Right, rectUi.Bottom);

                // Take the "extended" union of the first and last char's bounding box. 
                // milPointCaret = new Point(rectCaret.Left, rectCaret.Top);
                milPointCaret = new Point(rectCaret.Left, rectCaret.Bottom); 
 
                // Transform to root visual coordinates.
                transform = RenderScope.TransformToAncestor(compositionTarget.RootVisual); 
                transform.TryTransform(milPointTopLeft, out milPointTopLeft);
                transform.TryTransform(milPointBottomRight, out milPointBottomRight);
                transform.TryTransform(milPointCaret, out milPointCaret);
 
                // Transform to device units.
                milPointTopLeft = compositionTarget.TransformToDevice.Transform(milPointTopLeft); 
                milPointBottomRight = compositionTarget.TransformToDevice.Transform(milPointBottomRight); 
                milPointCaret = compositionTarget.TransformToDevice.Transform(milPointCaret);
 
                // Build COMPOSITIONFORM. COMPOSITIONFORM is window coodidate.
                NativeMethods.COMPOSITIONFORM compform = new NativeMethods.COMPOSITIONFORM();
                compform.dwStyle = NativeMethods.CFS_RECT;
                compform.rcArea.left   = ConvertToInt32(milPointTopLeft.X); 
                compform.rcArea.right  = ConvertToInt32(milPointBottomRight.X);
                compform.rcArea.top    = ConvertToInt32(milPointTopLeft.Y); 
                compform.rcArea.bottom = ConvertToInt32(milPointBottomRight.Y); 
                compform.ptCurrentPos  = new NativeMethods.POINT(ConvertToInt32(milPointCaret.X), ConvertToInt32(milPointCaret.Y));
 
                // Call IMM32 to set new candidate position to hIMC.
                // ImmSetCompositionWindow fails when
                //  - himc belongs to other threads.
                //  - fail to lock IMC. 
                // Those cases are ignorable for us.
                // In addition, it does not set win32 last error and we have no clue to handle error. 
                UnsafeNativeMethods.ImmSetCompositionWindow(new HandleRef(this, himc), ref compform); 
                UnsafeNativeMethods.ImmReleaseContext(new HandleRef(this, hwnd), new HandleRef(this, himc));
            } 
        }

        //
        // Hwnd disposed callback. 
        //
        ///  
        /// Critical - unparents the composition window visually 
        /// TreatAsSafe - while a DOS, this doesn't present any specific security threat.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void OnHwndDisposed(object sender, EventArgs args)
        {
            UpdateSource(_source, null); 
        }
 
        // 
        // update the composition string on the scope
        // 
        private void UpdateCompositionString(char[] resultChars, char[] compositionChars, int caretOffset, int deltaStart, int[] clauseInfo, byte[] attributes)
        {
            if (_handlingImeMessage)
            { 
                // We will be called reentrantly while completing compositions
                // in response to application listeners.  In that case, don't 
                // propegate events to listeners. 
                return;
            } 

            _handlingImeMessage = true;
            try
            { 
                //
                // Remove any existing composition adorner for display attribute. 
                // 
                if (_compositionAdorner != null)
                { 
                    _compositionAdorner.Uninitialize();
                    _compositionAdorner = null;
                }
 
                //
                // Build up an array of resultChars + compositionChars -- the complete span of changing text. 
                // 
                int resultLength;
                string compositionString = BuildCompositionString(resultChars, compositionChars, out resultLength); 

                if (compositionString == null)
                {
                    CompleteComposition(); 
                    return;
                } 
 
                //
                // Remember where the IME placed the caret. 
                //
                RecordCaretOffset(caretOffset, attributes, compositionString.Length);

                FrameworkTextComposition composition = TextStore.CreateComposition(_editor, this); 
                _compositionModifiedByApp = false;
 
                if (_startComposition == null) 
                {
                    Invariant.Assert(_endComposition == null); 

                    //
                    // Raise TextInputStart.
                    // 
                    bool handledbyApp = RaiseTextInputStartEvent(composition, resultLength, compositionString);
 
                    if (handledbyApp) 
                    {
                        CompleteComposition(); 
                        return;
                    }
                }
                else if (compositionChars != null) 
                {
                    // 
                    // Raise TextInputUpdate. 
                    //
                    bool handledByApp = RaiseTextInputUpdateEvent(composition, resultLength, compositionString); 

                    if (handledByApp)
                    {
                        CompleteComposition(); 
                        return;
                    } 
                } 

                if (compositionChars == null) 
                {
                    //
                    // Raise TextInput.
                    // 
                    bool handledByApp = RaiseTextInputEvent(composition, compositionString);
 
                    if (handledByApp) 
                    {
                        CompleteComposition(); 
                        return;
                    }
                }
 
                if (_startComposition != null)
                { 
                    SetCompositionAdorner(clauseInfo, attributes); 
                }
            } 
            finally
            {
                _handlingImeMessage = false;
            } 
        }
 
        // Attempts to build a string containing zero or more result chars 
        // followed by zero or more composition chars.
        // 
        // Will return null if an underlying TextBox has MaxLength property
        // that is exceeded by the new content.
        private string BuildCompositionString(char[] resultChars, char[] compositionChars, out int resultLength)
        { 
            int compositionLength = compositionChars == null ? 0 : compositionChars.Length;
            resultLength = resultChars == null ? 0 : resultChars.Length; 
            char[] compositionText; 

            if (resultChars == null) 
            {
                compositionText = compositionChars;
            }
            else if (compositionChars == null) 
            {
                compositionText = resultChars; 
            } 
            else
            { 
                compositionText = new char[resultLength + compositionLength];
                Array.Copy(resultChars, 0, compositionText, 0, resultLength);
                Array.Copy(compositionChars, 0, compositionText, resultLength, compositionLength);
            } 

            string compositionString = new string(compositionText); 
 
            if (!_editor.AcceptsRichContent)
            { 
                int charsToReplaceCount;

                if (_startComposition == null)
                { 
                    charsToReplaceCount = _editor.Selection.Start.GetOffsetToPosition(_editor.Selection.End);
                } 
                else 
                {
                    charsToReplaceCount = _startComposition.GetOffsetToPosition(_endComposition); 
                }

                compositionString = _editor._FilterText(compositionString, charsToReplaceCount);
            } 

            int originalLength = (compositionText == null) ? 0 : compositionText.Length; 
            return (compositionString.Length == originalLength) ? compositionString : null; 
        }
 
        // Caches the IME specified caret offset.
        // Value is the offset in unicode code points from the composition start.
        private void RecordCaretOffset(int caretOffset, byte[] attributes, int compositionLength)
        { 
            // Use the suggested value if it is on ATTR_INPUT, otherwise set the caret at the end of
            // composition string. So it always stays where the new char is inserted. 
            if ((attributes != null) && 
                // If the next char of the cursorPos is INPUTATTR.
                (((caretOffset >= 0) && 
                  (caretOffset < attributes.Length) &&
                  (attributes[caretOffset] == NativeMethods.ATTR_INPUT)) ||
                // If the prev char os the cursorPos is INPUTATTR.
                 ((caretOffset > 0) && 
                  ((caretOffset - 1) < attributes.Length) &&
                  (attributes[caretOffset - 1] == NativeMethods.ATTR_INPUT)))) 
            { 
                _caretOffset = caretOffset;
            } 
            else
            {
                _caretOffset = -1;
            } 
        }
 
        // Raises a public TextInputStart event. 
        // Returns true if a listener handles the event or modifies document state.
        ///  
        /// Critical - calls critical (TextCompositionManager) code.
        /// TreatAsSafe - doesn't accept or return critical information.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool RaiseTextInputStartEvent(FrameworkTextComposition composition, int resultLength, string compositionString)
        { 
            composition.Stage = TextCompositionStage.None; 
            composition.SetCompositionPositions(_editor.Selection.Start, _editor.Selection.End, compositionString);
 
            // PUBLIC event:
            bool handled = TextCompositionManager.StartComposition(composition);

            if (handled || 
                composition.PendingComplete ||
                _compositionModifiedByApp) 
            { 
                return true;
            } 

            // UpdateCompositionText raises a PUBLIC EVENT....
            UpdateCompositionText(composition, resultLength, true /* includeResultText */, out _startComposition, out _endComposition);
 
            if (_compositionModifiedByApp)
            { 
                return true; 
            }
 
            RegisterMouseListeners();

            return false;
        } 

        // Raises a public TextInputUpdate event. 
        // Returns true if a listener handles the event or modifies document state. 
        /// 
        /// Critical - calls critical (TextCompositionManager) code. 
        /// TreatAsSafe - doesn't accept or return critical information.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private bool RaiseTextInputUpdateEvent(FrameworkTextComposition composition, int resultLength, string compositionString) 
        {
            composition.Stage = TextCompositionStage.Started; 
            composition.SetCompositionPositions(_startComposition, _endComposition, compositionString); 

            // PUBLIC event: 
            bool handled = TextCompositionManager.UpdateComposition(composition);

            if (handled ||
                composition.PendingComplete || 
                _compositionModifiedByApp)
            { 
                return true; 
            }
 
            // UpdateCompositionText raises a PUBLIC EVENT....
            UpdateCompositionText(composition, resultLength, false /* includeResultText */, out _startComposition, out _endComposition);

            if (_compositionModifiedByApp) 
            {
                return true; 
            } 

            return false; 
        }

        // Raises a public TextInput event.
        // Returns true if a listener handles the event or modifies document state. 
        /// 
        /// Critical - calls critical (TextCompositionManager) code. 
        /// TreatAsSafe - doesn't accept or return critical information. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool RaiseTextInputEvent(FrameworkTextComposition composition, string compositionString)
        {
            composition.Stage = TextCompositionStage.Started;
            composition.SetResultPositions(_startComposition, _endComposition, compositionString); 

            _startComposition = null; 
            _endComposition = null; 

            UnregisterMouseListeners(); 

            _handledByEditorListener = false;

            // PUBLIC event: 
            TextCompositionManager.CompleteComposition(composition);
 
            _compositionUndoUnit = null; 

            return (!_handledByEditorListener || composition.PendingComplete || _compositionModifiedByApp); 
        }

        // Inserts composition text into the document.
        // Raises public text, selection changed events. 
        // Called by default editor TextInputEvent handler.
        internal void UpdateCompositionText(FrameworkTextComposition composition) 
        { 
            ITextPointer start;
            ITextPointer end; 

            UpdateCompositionText(composition, 0, true /* includeResultText */
                                                                              , out start, out end);
        } 

        // Inserts composition text into the document. 
        // Raises public text, selection changed events. 
        // Returns the position of the inserted text.  If includeResultText is
        // true, start/end will cover all the inserted text.  Otherwise, text 
        // from offset 0 to resultLength is omitted from start/end.
        internal void UpdateCompositionText(FrameworkTextComposition composition, int resultLength, bool includeResultText, out ITextPointer start, out ITextPointer end)
        {
            start = null; 
            end = null;
 
            if (_compositionModifiedByApp) 
            {
                // If the app has modified the document since this event was raised 
                // (by hooking a TextInput event), then we don't know what to do,
                // so do nothing.
                return;
            } 

            _handledByEditorListener = true; 
 
            UndoCloseAction undoCloseAction = UndoCloseAction.Rollback;
            OpenCompositionUndoUnit(); 

            try
            {
                _editor.Selection.BeginChange(); 
                try
                { 
                    // 

                    ITextRange range; 
                    string text;

                    if (composition._ResultStart != null)
                    { 
                        range = new TextRange(composition._ResultStart, composition._ResultEnd);
                        text = composition.Text; 
                    } 
                    else
                    { 
                        range = new TextRange(composition._CompositionStart, composition._CompositionEnd);
                        text = composition.CompositionText;
                    }
 
                    _editor.SetText(range, text, InputLanguageManager.Current.CurrentInputLanguage);
 
                    if (includeResultText) 
                    {
                        start = range.Start; 
                    }
                    else
                    {
                        start = range.Start.CreatePointer(resultLength, LogicalDirection.Forward); 
                    }
                    end = range.End; 
 
                    ITextPointer caretPosition = _caretOffset >= 0 ? start.CreatePointer(_caretOffset, LogicalDirection.Forward) : end;
                    _editor.Selection.Select(caretPosition, caretPosition); 
                }
                finally
                {
                    // We're about to raise the public event. 
                    // Set a flag so we can detect app changes.
                    _compositionModifiedByApp = false; 
 
                    _editor.Selection.EndChange();
                } 

                undoCloseAction = UndoCloseAction.Commit;
            }
            finally 
            {
                CloseCompositionUndoUnit(undoCloseAction, end); 
            } 
        }
 
        // Decorates the composition with IME specified underlining.
        private void SetCompositionAdorner(int[] clauseInfo, byte[] attributes)
        {
            if ((clauseInfo != null) && (attributes != null)) 
            {
                for (int i = 0; i < clauseInfo.Length - 1; i++) 
                { 
                    ITextPointer startClause = _startComposition.CreatePointer(clauseInfo[i], LogicalDirection.Backward);
                    ITextPointer endClause = _startComposition.CreatePointer(clauseInfo[i + 1], LogicalDirection.Forward); 

                    if (_compositionAdorner == null)
                    {
                        _compositionAdorner = new CompositionAdorner(_editor.TextView); 
                        _compositionAdorner.Initialize(_editor.TextView);
                    } 
 
                    //
                    // 


                    UnsafeNativeMethods.TF_DISPLAYATTRIBUTE displayAttribute = new UnsafeNativeMethods.TF_DISPLAYATTRIBUTE();
 
                    displayAttribute.crLine.type = UnsafeNativeMethods.TF_DA_COLORTYPE.TF_CT_COLORREF;
                    displayAttribute.crLine.indexOrColorRef = 0; 
                    displayAttribute.lsStyle = UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_NONE; 
                    displayAttribute.fBoldLine = false;
 
                    switch (attributes[clauseInfo[i]])
                    {
                        case NativeMethods.ATTR_INPUT:
                            displayAttribute.lsStyle = UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT; 
                            break;
 
                        case NativeMethods.ATTR_TARGET_CONVERTED: 
                            displayAttribute.lsStyle = UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID;
                            displayAttribute.fBoldLine = true; 
                            break;

                        case NativeMethods.ATTR_CONVERTED:
                            displayAttribute.lsStyle = UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID; 
                            break;
 
                        case NativeMethods.ATTR_TARGET_NOTCONVERTED: 
                            displayAttribute.lsStyle = UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID;
                            break; 

                        case NativeMethods.ATTR_INPUT_ERROR:
                            break;
 
                        case NativeMethods.ATTR_FIXEDCONVERTED:
                            break; 
                    } 

#if UNUSED_IME_HIGHLIGHT_LAYER 
                                // Demand create the highlight layer.
                                if (_highlightLayer == null)
                                {
                                    _highlightLayer = new DisplayAttributeHighlightLayer(); 
                                }
 
                                // ToDo: Need to pass the foreground and background color of the composition 
                                _highlightLayer.Add(startClause, endClause, /*TextDecorationCollection:*/null);
#endif 

                    TextServicesDisplayAttribute textServiceDisplayAttribute = new TextServicesDisplayAttribute(displayAttribute);

                    // Add the attribute range into CompositionAdorner. 
                    _compositionAdorner.AddAttributeRange(startClause, endClause, textServiceDisplayAttribute);
                } 
 
#if UNUSED_IME_HIGHLIGHT_LAYER
                            if (_highlightLayer != null) 
                            {
                                _editor.TextContainer.Highlights.AddLayer(_highlightLayer);
                            }
#endif 

                if (_compositionAdorner != null) 
                { 
                    // Update the layout to get the acurated rectangle from calling GetRectangleFromTextPosition
                    _editor.TextView.RenderScope.UpdateLayout(); 

                    // Invalidate the composition adorner to apply the composition attribute ranges.
                    _compositionAdorner.InvalidateAdorner();
                } 
            }
        } 
 
        // Start listening mouse event for MSIME mouse operation.
        private void RegisterMouseListeners() 
        {
            UiScope.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(OnMouseButtonEvent);
            UiScope.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(OnMouseButtonEvent);
            UiScope.PreviewMouseRightButtonDown += new MouseButtonEventHandler(OnMouseButtonEvent); 
            UiScope.PreviewMouseRightButtonUp += new MouseButtonEventHandler(OnMouseButtonEvent);
            UiScope.PreviewMouseMove += new MouseEventHandler(OnMouseEvent); 
        } 

        // Stop listening mouse event for MSIME mouse operation. 
        private void UnregisterMouseListeners()
        {
            if (this.UiScope != null)
            { 
                UiScope.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(OnMouseButtonEvent);
                UiScope.PreviewMouseLeftButtonUp -= new MouseButtonEventHandler(OnMouseButtonEvent); 
                UiScope.PreviewMouseRightButtonDown -= new MouseButtonEventHandler(OnMouseButtonEvent); 
                UiScope.PreviewMouseRightButtonUp -= new MouseButtonEventHandler(OnMouseButtonEvent);
                UiScope.PreviewMouseMove -= new MouseEventHandler(OnMouseEvent); 
            }
        }

        // 
        // WM_IME_REQUEST handler
        // 
        ///  
        /// Critical - This can be used to spoof input and it takes IntPtr from untrusted sources
        ///  
        [SecurityCritical]
        private IntPtr OnWmImeRequest(IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            IntPtr lret = IntPtr.Zero ; 

            switch ( (int) wParam) 
            { 
                case NativeMethods.IMR_RECONVERTSTRING:
                    lret = OnWmImeRequest_ReconvertString(lParam, ref handled, false); 
                    break;

                case NativeMethods.IMR_CONFIRMRECONVERTSTRING:
                    lret = OnWmImeRequest_ConfirmReconvertString(lParam, ref handled); 
                    break;
 
                case NativeMethods.IMR_QUERYCHARPOSITION: 
                    break;
 
                case NativeMethods.IMR_DOCUMENTFEED:
                    lret = OnWmImeRequest_ReconvertString(lParam, ref handled, true);
                    break;
            } 

            return lret; 
        } 

        // 
        // WM_IME_REQUEST/IMR_RECONVERTSTRING handler
        //
        /// 
        ///     Crtical: This code calls into PtrToStruct which is marked critical.It can also be used to spoof input 
        /// 
        [SecurityCritical] 
        private IntPtr OnWmImeRequest_ReconvertString(IntPtr lParam, ref bool handled, bool fDocFeed) 
        {
 
            if (!fDocFeed)
            {
                _isReconvReady = false;
            } 

            if (!IsInKeyboardFocus) 
            { 
                return IntPtr.Zero ;
            } 

            ITextRange range;

            // 
            // If there is the composition string, we use it. Otherwise we use the current selection.
            // 
            if (fDocFeed && (_startComposition != null) && (_endComposition != null)) 
            {
                range = new TextRange(_startComposition, _endComposition); 
            }
            else
            {
                range = _editor.Selection; 
            }
 
            string target = range.Text; 

            int requestSize = Marshal.SizeOf(typeof(NativeMethods.RECONVERTSTRING)) + (target.Length * Marshal.SizeOf(typeof(short))) + ((_maxSrounding + 1) * Marshal.SizeOf(typeof(short)) * 2); 
            IntPtr lret = new IntPtr( requestSize);

            if (lParam != IntPtr.Zero)
            { 
                 int offsetStart;
                 string surrounding = GetSurroundingText(range, out offsetStart); 
 
                 // Create RECONVERTSTRING structure from lParam.
                 NativeMethods.RECONVERTSTRING reconv = (NativeMethods.RECONVERTSTRING)Marshal.PtrToStructure(lParam, typeof(NativeMethods.RECONVERTSTRING)); 

                 reconv.dwSize = requestSize;
                 reconv.dwVersion = 0;                                                         // must be 0
                 reconv.dwStrLen = surrounding.Length;                                         // in char count 
                 reconv.dwStrOffset = Marshal.SizeOf(typeof(NativeMethods.RECONVERTSTRING));   // in byte count
                 reconv.dwCompStrLen = target.Length;                                          // in char count 
                 reconv.dwCompStrOffset = offsetStart * Marshal.SizeOf(typeof(short));         // in byte count 
                 reconv.dwTargetStrLen = target.Length;                                        // in char count
                 reconv.dwTargetStrOffset = offsetStart * Marshal.SizeOf(typeof(short));       // in byte count 

                 if (!fDocFeed)
                 {
                     // 
                     // If this is IMR_RECONVERTSTRING, we cache it. So we can refer it later when we get
                     // IMR_CONFIRMRECONVERTSTRING message. 
                     // 
                     _reconv = reconv;
                     _isReconvReady = true; 
                 }

                 // Copy the strucuture back to lParam.
                 Marshal.StructureToPtr(reconv, lParam, true); 

                 StoreSurroundingText(lParam, surrounding); 
            } 

            handled = true; 
            return lret;
        }

        ///  
        ///     Crtical: unsafe code to manipulate pointer.
        ///              This should not be called unless we're sure the first param is the pointer to 
        ///              NativeMethods.RECONVERTSTRING. 
        /// 
        [SecurityCritical] 
        private unsafe static void StoreSurroundingText(IntPtr reconv, string surrounding)
        {
            // Copy the string to the pointer right after the structure.
            byte *p = (byte *)reconv.ToPointer(); 
            p += Marshal.SizeOf(typeof(NativeMethods.RECONVERTSTRING));
            Marshal.Copy(surrounding.ToCharArray(), 0, new IntPtr((void *)p), surrounding.Length); 
        } 

        // 
        // Get the surrounding text of the given range.
        // The offsetStart is out param to return the offset of the given range in the returned surrounding text.
        //
        private static string GetSurroundingText(ITextRange range, out int offsetStart) 
        {
            ITextPointer navigator; 
            bool done; 
            string surrounding = "";
            int bufLength; 

            //
            // Get the previous text of the given range.
            // 
            navigator = range.Start.CreatePointer();
            done = false; 
            bufLength = _maxSrounding; 
            while (!done && (bufLength > 0))
            { 
                switch (navigator.GetPointerContext(LogicalDirection.Backward))
                {
                    case TextPointerContext.Text:
                        char[] buffer = new char[bufLength]; 
                        int copied = ((TextPointer)navigator).GetTextInRun(LogicalDirection.Backward, buffer, 0, buffer.Length);
                        Invariant.Assert(copied != 0); 
                        navigator.MoveByOffset(0 - copied); 
                        bufLength -= copied;
                        surrounding = surrounding.Insert(0, new string(buffer, 0, copied)); 
                        break;

                    case TextPointerContext.EmbeddedElement:
                        done = true; 
                        break;
 
                    case TextPointerContext.ElementStart: 
                    case TextPointerContext.ElementEnd:
                        // ignore the inline element. 
                        if (!navigator.GetElementType(LogicalDirection.Backward).IsSubclassOf(typeof(Inline)))
                        {
                            done = true;
                        } 
                        navigator.MoveToNextContextPosition(LogicalDirection.Backward);
                        break; 
 

                    case TextPointerContext.None: 
                        done = true;
                        break;

                    default: 
                        navigator.MoveToNextContextPosition(LogicalDirection.Backward);
                        break; 
                } 
            }
 
            // offsetStart is the amount of the current surroundingText.
            offsetStart = surrounding.Length;

            // 
            // add the text in the given range.
            // 
            surrounding += range.Text; 

            // 
            // Get the following text of the given range.
            //
            navigator = range.End.CreatePointer();
            done = false; 
            bufLength = _maxSrounding;
            while (!done && (bufLength > 0)) 
            { 
                switch (navigator.GetPointerContext(LogicalDirection.Forward))
                { 
                    case TextPointerContext.Text:
                        char[] buffer = new char[bufLength];
                        int copied = ((TextPointer)navigator).GetTextInRun(LogicalDirection.Forward, buffer, 0, buffer.Length);
                        navigator.MoveByOffset(copied); 
                        bufLength -= copied;
                        surrounding += new string(buffer, 0, copied); 
                        break; 

                    case TextPointerContext.EmbeddedElement: 
                        done = true;
                        break;

                    case TextPointerContext.ElementStart: 
                    case TextPointerContext.ElementEnd:
                        // ignore the inline element. 
                        if (!navigator.GetElementType(LogicalDirection.Forward).IsSubclassOf(typeof(Inline))) 
                        {
                            done = true; 
                        }
                        navigator.MoveToNextContextPosition(LogicalDirection.Forward);
                        break;
 
                    case TextPointerContext.None:
                        done = true; 
                        break; 

                    default: 
                        navigator.MoveToNextContextPosition(LogicalDirection.Forward);
                        break;
                 }
             } 

             return surrounding; 
        } 

        // 
        // WM_IME_REQUEST/IMR_CONFIRMRECONVERTSTRING handler
        //
        /// 
        /// Critical - This can be used to spoof input and it takes IntPtr from untrusted sources 
        /// 
        [SecurityCritical] 
        private IntPtr OnWmImeRequest_ConfirmReconvertString(IntPtr lParam, ref bool handled) 
        {
            if (!IsInKeyboardFocus) 
            {
                return IntPtr.Zero ;
            }
 
            if (!_isReconvReady)
            { 
                return IntPtr.Zero ; 
            }
 
            NativeMethods.RECONVERTSTRING reconv = (NativeMethods.RECONVERTSTRING)Marshal.PtrToStructure(lParam, typeof(NativeMethods.RECONVERTSTRING));

            // If the entire string in RECONVERTSTRING has been changed, we don't handle it.
            if (_reconv.dwStrLen !=  reconv.dwStrLen) 
            {
                handled = true; 
                return IntPtr.Zero ; 
            }
 
            //
            // If the new CompStr was suggested by IME, we need to adjust the selection with it.
            //
            if ((_reconv.dwCompStrLen !=  reconv.dwCompStrLen) || 
                (_reconv.dwCompStrOffset != reconv.dwCompStrOffset))
            { 
                 ITextRange range = _editor.Selection; 

                 // 
                 // Create the start point from the selection
                 //
                 ITextPointer start = range.Start.CreatePointer(LogicalDirection.Backward);
 
                 // Move the start point to new  dwCompStrOffset.
                 start = MoveToNextCharPos(start, (reconv.dwCompStrOffset - _reconv.dwCompStrOffset) / Marshal.SizeOf(typeof(short))); 
                 // Create the end position and move this as dwCompStrLen. 
                 ITextPointer end = start.CreatePointer(LogicalDirection.Forward);
                 end = MoveToNextCharPos(end, reconv.dwCompStrLen); 

                 // Update the selection with new start and end.
                 _editor.Selection.Select(start, end);
            } 

            _isReconvReady = false; 
            handled = true; 
            return new IntPtr( 1 ) ;
        } 

        //
        // Move the TextPointer by offset in char count.
        // 
        private static ITextPointer MoveToNextCharPos(ITextPointer position, int offset)
        { 
            bool done = false; 
            if (offset < 0)
            { 
                while ((offset < 0) && !done)
                {
                    switch (position.GetPointerContext(LogicalDirection.Backward))
                    { 
                        case TextPointerContext.Text:
                            offset++; 
                            break; 
                        case TextPointerContext.None:
                            done = true; 
                            break;
                    }
                    position.MoveByOffset(-1);
                } 
            }
            else if (offset > 0) 
            { 
                while ((offset > 0) && !done)
                { 
                    switch (position.GetPointerContext(LogicalDirection.Forward))
                    {
                        case TextPointerContext.Text:
                            offset--; 
                            break;
                        case TextPointerContext.None: 
                            done = true; 
                            break;
                    } 
                    position.MoveByOffset(1);
                }
            }
 
            return position;
        } 
 
        //
        // Move the TextPointer by offset in char count. 
        //
        /// 
        /// Critical -    calls ImmGetProperty that could expose the current ime's capability.
        /// TreatAsSafe - the return value says only if IME is near caret Chinese IME. 
        ///               exposing this information is safe.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool IsReadingWindowIme()
        { 
           int prop = UnsafeNativeMethods.ImmGetProperty(new HandleRef(this, SafeNativeMethods.GetKeyboardLayout(0)), NativeMethods.IGP_PROPERTY);
           return (((prop & NativeMethods.IME_PROP_AT_CARET) == 0) || ((prop & NativeMethods.IME_PROP_SPECIAL_UI) != 0));
        }
 
        //
        // Mouse Button state was changed. 
        // 
       /// 
       /// Critical - calls critical code (InternalMouseEventHandler) 
       /// TreatAsSafe - the mouse input here is ignored, but rather the mouse
       ///               is directly queried, so there is no spoofing possibility
       /// 
       [SecurityCritical, SecurityTreatAsSafe] 
       private void OnMouseButtonEvent(object sender, MouseButtonEventArgs e)
       { 
            e.Handled = InternalMouseEventHandler(); 
        }
 
        //
        // Mouse was moved.
        //
        ///  
        /// Critical - calls critical code (InternalMouseEventHandler)
        /// TreatAsSafe - the mouse input here is ignored, but rather the mouse 
        ///               is directly queried, so there is no spoofing possibility 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void OnMouseEvent(object sender, MouseEventArgs e)
        {
            e.Handled = InternalMouseEventHandler();
        } 

        // 
        // The mouse event handler to generate MSIME message to IME listeners. 
        //
        ///  
        /// Critical - calls unmanaged code, sends WM_* messages, simulates mouse
        ///            messages to the IME. This could result in input spoofing.
        /// 
        [SecurityCritical] 
        private bool InternalMouseEventHandler()
        { 
            int btnState = 0; 
            if (Mouse.LeftButton == MouseButtonState.Pressed)
            { 
               btnState = 1; // IMEMOUSE_LDOWN
            }
            if (Mouse.RightButton == MouseButtonState.Pressed)
            { 
               btnState = 2; // IMEMOUSE_RDOWN
            } 
 
            Point point = Mouse.GetPosition(RenderScope);
            ITextView view; 
            ITextPointer positionCurrent;
            ITextPointer positionNext;
            Rect rectCurrent;
            Rect rectNext; 

            view = TextEditor.GetTextView(RenderScope); 
            // Validate layout information on TextView 
            if (!view.Validate(point))
            { 
                return false;
            }

            // Do the hittest. 
            positionCurrent = view.GetTextPositionFromPoint(point, false);
            if (positionCurrent == null) 
            { 
                return false;
            } 

            rectCurrent = view.GetRectangleFromTextPosition(positionCurrent);

            positionNext = positionCurrent.CreatePointer(); 
            if (positionNext == null)
            { 
                return false; 
            }
 
            if (point.X - rectCurrent.Left >= 0)
            {
                positionNext.MoveToNextInsertionPosition(LogicalDirection.Forward);
            } 
            else
            { 
                positionNext.MoveToNextInsertionPosition(LogicalDirection.Backward); 
            }
 
            rectNext = view.GetRectangleFromTextPosition(positionNext);

            int edge;
            int quadrant; 
            edge = _editor.TextContainer.Start.GetOffsetToPosition(positionCurrent);
            int startComposition = _editor.TextContainer.Start.GetOffsetToPosition(_startComposition); 
            int endComposition = _editor.TextContainer.Start.GetOffsetToPosition(_endComposition); 

            // 
            // IMEs care about only the composition string range.
            //

            if (edge < startComposition) 
            {
                return false; 
            } 

            if (edge > endComposition) 
            {
                return false;
            }
 
            if (rectNext.Left == rectCurrent.Left)
            { 
                // if rectNext.Left == rectCurrent.Left, the width of char is 0 and mouse click points there. 
                // there is no quadrent. So we alwasys make it 0.
                quadrant = 0; 
            }
            else
            {
                if (point.X - rectCurrent.Left >= 0) 
                {
                    if ((((point.X - rectCurrent.Left) * 4) / (rectNext.Left - rectCurrent.Left)) <= 1) 
                        quadrant = 2; 
                    else
                        quadrant = 3; 
                }
                else
                {
                    if (((point.X - rectNext.Left) * 4) / (rectCurrent.Left - rectNext.Left) <= 3) 
                        quadrant = 0;
                    else 
                        quadrant = 1; 
                }
            } 

            //
            // IMEs care about only the composition string range.
            // If the quadrant is outside of the range, we don't do SendMessage. 
            //
 
            if ((edge == startComposition) && (quadrant <= 1)) 
            {
                return false; 
            }

            if ((edge == endComposition) && (quadrant >= 2))
            { 
                return false;
            } 
 
            //
            // The edge must be relative to the composition string. 
            //
            edge -= startComposition;

            int wParam = (edge << 16) + (quadrant << 8) + btnState; 

            IntPtr hwnd = IntPtr.Zero; 
 
            new UIPermission(UIPermissionWindow.AllWindows).Assert();//Blessed Assert
            try 
            {
                hwnd = ((IWin32Window)_source).Handle;
            }
            finally 
            {
                CodeAccessPermission.RevertAssert(); 
            } 

            IntPtr himc = UnsafeNativeMethods.ImmGetContext(new HandleRef(this, hwnd)); 

            IntPtr lret = IntPtr.Zero;
            if (himc != IntPtr.Zero)
            { 
                IntPtr hwndDefIme = IntPtr.Zero;
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();//Blessed Assert 
                try 
                {
                    hwndDefIme = UnsafeNativeMethods.ImmGetDefaultIMEWnd(new HandleRef(this, hwnd)); 
                    lret = UnsafeNativeMethods.SendMessage(hwndDefIme, s_MsImeMouseMessage, new IntPtr(wParam), himc);
                }
                finally
                { 
                    SecurityPermission.RevertAssert();
                } 
            } 

            // We eat this event if IME handled. 
            return (lret != IntPtr.Zero) ? true : false;
        }

        // Opens a composition undo unit. Opens the compsed composition undo unit if it exist on the top 
        // of the stack. Otherwise, create new composition undo unit and add it to the undo manager and
        // making it as the opened undo unit. 
        private void OpenCompositionUndoUnit() 
        {
            UndoManager undoManager; 
            DependencyObject parent;

            parent = _editor.TextContainer.Parent;
            undoManager = UndoManager.GetUndoManager(parent); 

            if (undoManager != null && undoManager.IsEnabled && undoManager.OpenedUnit == null) 
            { 
                if (_compositionUndoUnit != null && _compositionUndoUnit == undoManager.LastUnit && !_compositionUndoUnit.Locked)
                { 
                    // Opens a closed composition undo unit on the top of the stack.
                    undoManager.Reopen(_compositionUndoUnit);
                }
                else 
                {
                    _compositionUndoUnit = new TextParentUndoUnit(_editor.Selection); 
 
                    // Add the given composition undo unit to the undo manager and making it
                    // as the opened undo unit. 
                    undoManager.Open(_compositionUndoUnit);
                }
            }
            else 
            {
                _compositionUndoUnit = null; 
            } 
        }
 
        // Closes an opened composition unit and adding it to the containing unit's undo stack.
        private void CloseCompositionUndoUnit(UndoCloseAction undoCloseAction, ITextPointer compositionEnd)
        {
            UndoManager undoManager; 
            DependencyObject parent;
 
            parent = _editor.TextContainer.Parent; 
            undoManager = UndoManager.GetUndoManager(parent);
 
            if (undoManager != null && undoManager.IsEnabled)
            {
                if (_compositionUndoUnit != null)
                { 
                    // Closes an opened composition unit and commit it to add the composition
                    // undo unit into the containing unit's undo stack. 
                    if (undoCloseAction == UndoCloseAction.Commit) 
                    {
                        _compositionUndoUnit.RecordRedoSelectionState(compositionEnd, compositionEnd); 
                    }
                    undoManager.Close(_compositionUndoUnit, undoCloseAction);
                }
            } 
            else
            { 
                _compositionUndoUnit = null; 
            }
        } 

        // Converts a double into a 32 bit integer, truncating values that
        // exceed Int32.MinValue or Int32.MaxValue.
        private int ConvertToInt32(double value) 
        {
            int i; 
 
            if (Double.IsNaN(value))
            { 
                // (int)value is 0x80000000. So we should assign Int32.MinValue.
                i = Int32.MinValue;
            }
            else if (value < Int32.MinValue) 
            {
                i = Int32.MinValue; 
            } 
            else if (value > Int32.MaxValue)
            { 
                i = Int32.MaxValue;
            }
            else
            { 
                i = Convert.ToInt32(value);
            } 
 
            return i;
        } 

        private void OnTextContainerChange(object sender, TextContainerChangeEventArgs args)
        {
            if (args.IMECharCount > 0 && (args.TextChange == TextChangeType.ContentAdded || args.TextChange == TextChangeType.ContentRemoved)) 
            {
                _compositionModifiedByApp = true; 
            } 
        }
 
        //------------------------------------------------------
        //
        //  Private Properties
        // 
        //-----------------------------------------------------
 
        private UIElement RenderScope 
        {
            get { return _editor.TextView == null ? null : _editor.TextView.RenderScope; } 
        }

        private FrameworkElement UiScope
        { 
            get { return (_editor == null) ? null : _editor.UiScope; }
        } 
 
        private bool IsReadOnly
        { 
            get
            {
                 return ((bool)UiScope.GetValue(TextEditor.IsReadOnlyProperty) || _editor.IsReadOnly);
            } 
        }
 
        private bool IsInKeyboardFocus 
        {
            get 
            {
                if (_editor == null)
                {
                    return false; 
                }
 
                if (UiScope == null) 
                {
                    return false; 
                }

                if (!UiScope.IsKeyboardFocused)
                { 
                    return false;
                } 
                return true; 
            }
        } 

        //------------------------------------------------------
        //
        //  Private Fields 
        //
        //------------------------------------------------------ 
 
        #region Private Fields
 
        //
        // HwndSource of this instance of ImmComposition.
        //
        [SecurityCritical] 
        private HwndSource _source;
 
        // 
        // TextEditor of the current focus element.
        // 
        private TextEditor _editor;

        //
        // The current start position of the compositon string. 
        // This is null if the composition string does not exist.
        // 
        private ITextPointer _startComposition; 

        // 
        // The current end position of the compositon string.
        // This is null if the composition string does not exist.
        //
        private ITextPointer _endComposition; 

        // 
        // The offset in chars from the start of the composition to the IME caret. 
        //
        private int _caretOffset; 

#if UNUSED_IME_HIGHLIGHT_LAYER
        //
        // Highlight layer forLevel3 composition drawing. 
        //
        private DisplayAttributeHighlightLayer _highlightLayer; 
#endif 

        // 
        // CompositionAdorner for displaying the composition attributes.
        //
        private CompositionAdorner _compositionAdorner;
 
        //
        // List of ImmComposition instances. 
        // 
        private static Hashtable _list = new Hashtable(1);
 
        //
        // Dash length of the compositon string underline.
        //
        private const double _dashLength = 2.0; 

        // 
        // Max surrounding char count for RECONVERTSTRING/DOCFEED. 
        //
        private const int _maxSrounding = 0x20; 

        //
        //  Cached RECONVERTSTRING structure for IMR_CONFIRMRECONVERTSTRING message handling.
        // 
        private NativeMethods.RECONVERTSTRING _reconv;
 
        // 
        //  True if the cached RECONVERTSTRING structure is ready.
        // 
        private bool _isReconvReady;

        //
        //  MSIME mouse operation message. 
        //
        ///  
        ///     Critical: This code registers a custom message and calls into a critical method 
        /// 
        [SecurityCritical] 
        private static int s_MsImeMouseMessage = UnsafeNativeMethods.RegisterWindowMessage("MSIMEMouseOperation");

        // This is the composition undo unit.
        private TextParentUndoUnit _compositionUndoUnit; 

        // Reentry flag, set true while handling WM_IME_UPDATE, WM_IME_CHAR. 
        private bool _handlingImeMessage; 

        // If this is true, call UpdateNearCaretCompositionWindow() at the next layout update. 
        private bool _updateCompWndPosAtNextLayoutUpdate;

        // Set true if an application listener modified the document content
        // or selection inside a TextInput* event. 
        private bool _compositionModifiedByApp;
 
        // Flag set true when TextInput events are handled by the default 
        // TextEditor listener -- not intercepted by an application listener.
        private bool _handledByEditorListener; 

        // Set true while completing a composition from OnLostFocus.
        private bool _losingFocus;
 
        #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