Example #1
0
        // Set candidate window position.
        // Borrowed from https://github.com/chromium/chromium/blob/master/ui/base/ime/win/imm32_manager.cc
        public void SetCandidateWindow(TsfSharp.Rect caretRect)
        {
            int x = caretRect.Left;
            int y = caretRect.Top;

            if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE)
            {
                // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
                // when a user disables TSF (Text Service Framework) and CUAS (Cicero
                // Unaware Application Support).
                // On the other hand, when a user enables TSF and CUAS, Chinese IMEs
                // ignore the position of the current system caret and uses the
                // parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
                // parameter CFS_CANDIDATEPOS.
                // Therefore, we do not only call ::ImmSetCandidateWindow() but also
                // set the positions of the temporary system caret.
                var candidateForm = new NativeMethods.CANDIDATEFORM();
                candidateForm.dwStyle        = NativeMethods.CFS_CANDIDATEPOS;
                candidateForm.ptCurrentPos.X = x;
                candidateForm.ptCurrentPos.Y = y;
                NativeMethods.ImmSetCandidateWindow(_defaultImc, ref candidateForm);
            }

            if (PRIMARYLANGID(_inputLanguageId) == LANG_JAPANESE)
            {
                NativeMethods.SetCaretPos(x, caretRect.Bottom);
            }
            else
            {
                NativeMethods.SetCaretPos(x, y);
            }

            // Set composition window position also to ensure move the candidate window position.
            var compositionForm = new NativeMethods.COMPOSITIONFORM();

            compositionForm.dwStyle        = NativeMethods.CFS_POINT;
            compositionForm.ptCurrentPos.X = x;
            compositionForm.ptCurrentPos.Y = y;
            NativeMethods.ImmSetCompositionWindow(_defaultImc, ref compositionForm);

            if (PRIMARYLANGID(_inputLanguageId) == LANG_KOREAN)
            {
                // Chinese IMEs and Japanese IMEs require the upper-left corner of
                // the caret to move the position of their candidate windows.
                // On the other hand, Korean IMEs require the lower-left corner of the
                // caret to move their candidate windows.
                y += kCaretMargin;
            }

            // Need to return here since some Chinese IMEs would stuck if set
            // candidate window position with CFS_EXCLUDE style.
            if (PRIMARYLANGID(_inputLanguageId) == LANG_CHINESE)
            {
                return;
            }

            // Japanese IMEs and Korean IMEs also use the rectangle given to
            // ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
            // to move their candidate windows when a user disables TSF and CUAS.
            // Therefore, we also set this parameter here.
            var excludeRectangle = new NativeMethods.CANDIDATEFORM();

            compositionForm.dwStyle        = NativeMethods.CFS_EXCLUDE;
            compositionForm.ptCurrentPos.X = x;
            compositionForm.ptCurrentPos.Y = y;
            compositionForm.rcArea.Left    = x;
            compositionForm.rcArea.Top     = y;
            compositionForm.rcArea.Right   = caretRect.Right;
            compositionForm.rcArea.Bottom  = caretRect.Bottom;
            NativeMethods.ImmSetCandidateWindow(_defaultImc, ref excludeRectangle);
        }
Example #2
0
        /// <summary>
        /// 候補文字ウィンドウの位置を設定
        /// </summary>
        /// <param name="hWnd"></param>
        private void SetCandidateWindowPos(IntPtr hWnd)
        {
            IntPtr hImc = NativeMethods.ImmGetContext(hWnd);
            if (hImc != null)
            {
                NativeMethods.CANDIDATEFORM cndFrm = new NativeMethods.CANDIDATEFORM();
                cndFrm.ptCurrentPos.X = caret.GetPos().X;
                cndFrm.ptCurrentPos.Y = caret.GetPos().Y;

                cndFrm.dwStyle = NativeMethods.CFS_CANDIDATEPOS;// 候補文字ウィンドウの位置を指定する。

                NativeMethods.ImmSetCandidateWindow(hImc, ref cndFrm);

                NativeMethods.ImmReleaseContext(hWnd, hImc);
            }
        }
Example #3
0
        private void OnWmImeNotify(IntPtr hwnd, IntPtr wParam)
        {
            IntPtr himc;

            // we don't have to do anything if _editor is null.
            if (!IsInKeyboardFocus)
            {
                return;
            }

            if ((int)wParam == 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".
            }
        }
Example #4
0
        /// <summary>
        /// WndProc
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            //            LogWriter logWriter = new LogWriter(@"C:\log\MyTextbox.txt");
            //            logWriter.Write(m.ToString());

            IntPtr hIMC;

            switch (m.Msg)
            {
                case NativeMethods.WM_CHAR:
                    hIMC = NativeMethods.ImmGetContext(this.Handle);
                    if (NativeMethods.ImmGetOpenStatus(hIMC) == 0)
                    {
                        char chr = Convert.ToChar(m.WParam.ToInt32() & 0xff);
                        Insert(chr.ToString());
            //                        caret.SetPos(caret.GetPos().X + , caret.GetPos().Y);
                    }
                    NativeMethods.ImmReleaseContext(this.Handle, hIMC);
                    Invalidate();
                    break;
                case NativeMethods.WM_IME_STARTCOMPOSITION:
                    hIMC = NativeMethods.ImmGetContext(this.Handle);

                    // ImmSetCompositionWindowとImmSetCandidateWindowがしっかり機能しているのかがイマイチ分からない。
                    // 変換ウィンドウの位置を設定
                    NativeMethods.COMPOSITIONFORM cf = new NativeMethods.COMPOSITIONFORM();
                    cf.dwStyle = NativeMethods.CFS_POINT;
                    cf.ptCurrentPos = new Point(100, 0);
                    cf.rcArea = new Rectangle();
                    NativeMethods.ImmSetCompositionWindow(hIMC, ref cf);

                    // 候補文字ウィンドウの位置調整を行う
                    NativeMethods.CANDIDATEFORM lpCandidate = new NativeMethods.CANDIDATEFORM();
                    lpCandidate.dwIndex = 0;
                    lpCandidate.dwStyle = NativeMethods.CFS_CANDIDATEPOS;
                    lpCandidate.ptCurrentPos = new Point(10, 50);
                    NativeMethods.ImmSetCandidateWindow(hIMC, ref lpCandidate);

                    NativeMethods.ImmReleaseContext(this.Handle, hIMC);
                    break;
                case NativeMethods.WM_IME_COMPOSITION:
                    this.ImeComposition(m);
                    break;
                case NativeMethods.WM_IME_ENDCOMPOSITION:

                    break;
                case NativeMethods.WM_IME_NOTIFY:
                    switch (m.WParam.ToInt32())
                    {
                        case NativeMethods.IMN_OPENCANDIDATE:
                            // 候補文字ウィンドウが表示された
                            this.SetCandidateWindowPos(m.HWnd);
                            break;
                        case NativeMethods.IMN_CLOSECANDIDATE:
                        case NativeMethods.IMN_CHANGECANDIDATE:
                        case NativeMethods.IMN_SETOPENSTATUS:
                            // 何もしない。
                            break;
                    }
                    break;

            }
            base.WndProc(ref m);
        }