aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs
blob: bc2c6f5e41d6e990ea7fbefd854002336e2750ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highligh
// 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);
	}
}