diff options
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs new file mode 100644 index 000000000..bc2c6f5e4 --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs @@ -0,0 +1,206 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using System.Windows; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.TextFormatting; + +using Tango.Scripting.Editors; +using Tango.Scripting.Editors.Document; +using Tango.Scripting.Editors.Rendering; +using Tango.Scripting.Editors.Utils; +using Draw = System.Drawing; + +namespace Tango.Scripting.Editors.Editing +{ + /// <summary> + /// Native API required for IME support. + /// </summary> + static class ImeNativeWrapper + { + [StructLayout(LayoutKind.Sequential)] + struct CompositionForm + { + public int dwStyle; + public POINT ptCurrentPos; + public RECT rcArea; + } + + [StructLayout(LayoutKind.Sequential)] + struct POINT + { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential)] + struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + struct LOGFONT + { + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName; + } + + const int CPS_CANCEL = 0x4; + const int NI_COMPOSITIONSTR = 0x15; + const int GCS_COMPSTR = 0x0008; + + public const int WM_IME_COMPOSITION = 0x10F; + public const int WM_IME_SETCONTEXT = 0x281; + public const int WM_INPUTLANGCHANGE = 0x51; + + [DllImport("imm32.dll")] + public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); + [DllImport("imm32.dll")] + internal static extern IntPtr ImmGetContext(IntPtr hWnd); + [DllImport("imm32.dll")] + internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd); + [DllImport("imm32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC); + [DllImport("imm32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0); + [DllImport("imm32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form); + [DllImport("imm32.dll", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font); + [DllImport("imm32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ImmGetCompositionFont(IntPtr hIMC, out LOGFONT font); + + [DllImport("msctf.dll")] + static extern int TF_CreateThreadMgr(out ITfThreadMgr threadMgr); + + [ThreadStatic] static bool textFrameworkThreadMgrInitialized; + [ThreadStatic] static ITfThreadMgr textFrameworkThreadMgr; + + public static ITfThreadMgr GetTextFrameworkThreadManager() + { + if (!textFrameworkThreadMgrInitialized) { + textFrameworkThreadMgrInitialized = true; + TF_CreateThreadMgr(out textFrameworkThreadMgr); + } + return textFrameworkThreadMgr; + } + + public static bool NotifyIme(IntPtr hIMC) + { + return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL); + } + + public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea) + { + if (textArea == null) + throw new ArgumentNullException("textArea"); + Rect textViewBounds = textArea.TextView.GetBounds(source); + Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); + CompositionForm form = new CompositionForm(); + form.dwStyle = 0x0020; + form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left); + form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top); + form.rcArea.left = (int)textViewBounds.Left; + form.rcArea.top = (int)textViewBounds.Top; + form.rcArea.right = (int)textViewBounds.Right; + form.rcArea.bottom = (int)textViewBounds.Bottom; + return ImmSetCompositionWindow(hIMC, ref form); + } + + public static bool SetCompositionFont(HwndSource source, IntPtr hIMC, TextArea textArea) + { + if (textArea == null) + throw new ArgumentNullException("textArea"); + LOGFONT lf = new LOGFONT(); + Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); + lf.lfFaceName = textArea.FontFamily.Source; + lf.lfHeight = (int)characterBounds.Height; + return ImmSetCompositionFont(hIMC, ref lf); + } + + static Rect GetBounds(this TextView textView, HwndSource source) + { + // this may happen during layout changes in AvalonDock, so we just return an empty rectangle + // in those cases. It should be refreshed immediately. + if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) + return EMPTY_RECT; + Rect displayRect = new Rect(0, 0, textView.ActualWidth, textView.ActualHeight); + return textView + .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual + .TransformToDevice(source.RootVisual); // rect on HWND + } + + static readonly Rect EMPTY_RECT = new Rect(0, 0, 0, 0); + + static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source) + { + VisualLine vl = textView.GetVisualLine(pos.Line); + if (vl == null) + return EMPTY_RECT; + // this may happen during layout changes in AvalonDock, so we just return an empty rectangle + // in those cases. It should be refreshed immediately. + if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) + return EMPTY_RECT; + TextLine line = vl.GetTextLine(pos.VisualColumn); + Rect displayRect; + // calculate the display rect for the current character + if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) { + displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle; + displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop)); + } else { + // if we are in virtual space, we just use one wide-space as character width + displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop), + new Size(textView.WideSpaceWidth, textView.DefaultLineHeight)); + } + // adjust to current scrolling + displayRect.Offset(-textView.ScrollOffset); + return textView + .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual + .TransformToDevice(source.RootVisual); // rect on HWND + } + } + + [ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface ITfThreadMgr + { + void Activate(out int clientId); + void Deactivate(); + void CreateDocumentMgr(out IntPtr docMgr); + void EnumDocumentMgrs(out IntPtr enumDocMgrs); + void GetFocus(out IntPtr docMgr); + void SetFocus(IntPtr docMgr); + void AssociateFocus(IntPtr hwnd, IntPtr newDocMgr, out IntPtr prevDocMgr); + void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus); + void GetFunctionProvider(ref Guid classId, out IntPtr funcProvider); + void EnumFunctionProviders(out IntPtr enumProviders); + void GetGlobalCompartment(out IntPtr compartmentMgr); + } +} |
