aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs
diff options
context:
space:
mode:
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.cs206
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);
+ }
+}