1 | // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
---|
2 | // |
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this |
---|
4 | // software and associated documentation files (the "Software"), to deal in the Software |
---|
5 | // without restriction, including without limitation the rights to use, copy, modify, merge, |
---|
6 | // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
---|
7 | // to whom the Software is furnished to do so, subject to the following conditions: |
---|
8 | // |
---|
9 | // The above copyright notice and this permission notice shall be included in all copies or |
---|
10 | // substantial portions of the Software. |
---|
11 | // |
---|
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
---|
13 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
---|
14 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
---|
15 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
---|
16 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
17 | // DEALINGS IN THE SOFTWARE. |
---|
18 | |
---|
19 | using System; |
---|
20 | using System.Linq; |
---|
21 | using System.Runtime.CompilerServices; |
---|
22 | using System.Runtime.InteropServices; |
---|
23 | using System.Security; |
---|
24 | using System.Windows; |
---|
25 | using System.Windows.Input; |
---|
26 | using System.Windows.Interop; |
---|
27 | using System.Windows.Media; |
---|
28 | using System.Windows.Media.TextFormatting; |
---|
29 | |
---|
30 | using ICSharpCode.AvalonEdit; |
---|
31 | using ICSharpCode.AvalonEdit.Document; |
---|
32 | using ICSharpCode.AvalonEdit.Rendering; |
---|
33 | using ICSharpCode.AvalonEdit.Utils; |
---|
34 | using Draw = System.Drawing; |
---|
35 | |
---|
36 | namespace ICSharpCode.AvalonEdit.Editing |
---|
37 | { |
---|
38 | /// <summary> |
---|
39 | /// Native API required for IME support. |
---|
40 | /// </summary> |
---|
41 | static class ImeNativeWrapper |
---|
42 | { |
---|
43 | [StructLayout(LayoutKind.Sequential)] |
---|
44 | struct CompositionForm |
---|
45 | { |
---|
46 | public int dwStyle; |
---|
47 | public POINT ptCurrentPos; |
---|
48 | public RECT rcArea; |
---|
49 | } |
---|
50 | |
---|
51 | [StructLayout(LayoutKind.Sequential)] |
---|
52 | struct POINT |
---|
53 | { |
---|
54 | public int x; |
---|
55 | public int y; |
---|
56 | } |
---|
57 | |
---|
58 | [StructLayout(LayoutKind.Sequential)] |
---|
59 | struct RECT |
---|
60 | { |
---|
61 | public int left; |
---|
62 | public int top; |
---|
63 | public int right; |
---|
64 | public int bottom; |
---|
65 | } |
---|
66 | |
---|
67 | [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] |
---|
68 | struct LOGFONT |
---|
69 | { |
---|
70 | public int lfHeight; |
---|
71 | public int lfWidth; |
---|
72 | public int lfEscapement; |
---|
73 | public int lfOrientation; |
---|
74 | public int lfWeight; |
---|
75 | public byte lfItalic; |
---|
76 | public byte lfUnderline; |
---|
77 | public byte lfStrikeOut; |
---|
78 | public byte lfCharSet; |
---|
79 | public byte lfOutPrecision; |
---|
80 | public byte lfClipPrecision; |
---|
81 | public byte lfQuality; |
---|
82 | public byte lfPitchAndFamily; |
---|
83 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName; |
---|
84 | } |
---|
85 | |
---|
86 | const int CPS_CANCEL = 0x4; |
---|
87 | const int NI_COMPOSITIONSTR = 0x15; |
---|
88 | const int GCS_COMPSTR = 0x0008; |
---|
89 | |
---|
90 | public const int WM_IME_COMPOSITION = 0x10F; |
---|
91 | public const int WM_IME_SETCONTEXT = 0x281; |
---|
92 | public const int WM_INPUTLANGCHANGE = 0x51; |
---|
93 | |
---|
94 | [DllImport("imm32.dll")] |
---|
95 | public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); |
---|
96 | [DllImport("imm32.dll")] |
---|
97 | internal static extern IntPtr ImmGetContext(IntPtr hWnd); |
---|
98 | [DllImport("imm32.dll")] |
---|
99 | internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd); |
---|
100 | [DllImport("imm32.dll")] |
---|
101 | [return: MarshalAs(UnmanagedType.Bool)] |
---|
102 | internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC); |
---|
103 | [DllImport("imm32.dll")] |
---|
104 | [return: MarshalAs(UnmanagedType.Bool)] |
---|
105 | static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0); |
---|
106 | [DllImport("imm32.dll")] |
---|
107 | [return: MarshalAs(UnmanagedType.Bool)] |
---|
108 | static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form); |
---|
109 | [DllImport("imm32.dll", CharSet = CharSet.Unicode)] |
---|
110 | [return: MarshalAs(UnmanagedType.Bool)] |
---|
111 | static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font); |
---|
112 | [DllImport("imm32.dll")] |
---|
113 | [return: MarshalAs(UnmanagedType.Bool)] |
---|
114 | static extern bool ImmGetCompositionFont(IntPtr hIMC, out LOGFONT font); |
---|
115 | |
---|
116 | [DllImport("msctf.dll")] |
---|
117 | static extern int TF_CreateThreadMgr(out ITfThreadMgr threadMgr); |
---|
118 | |
---|
119 | [ThreadStatic] static bool textFrameworkThreadMgrInitialized; |
---|
120 | [ThreadStatic] static ITfThreadMgr textFrameworkThreadMgr; |
---|
121 | |
---|
122 | public static ITfThreadMgr GetTextFrameworkThreadManager() |
---|
123 | { |
---|
124 | if (!textFrameworkThreadMgrInitialized) { |
---|
125 | textFrameworkThreadMgrInitialized = true; |
---|
126 | TF_CreateThreadMgr(out textFrameworkThreadMgr); |
---|
127 | } |
---|
128 | return textFrameworkThreadMgr; |
---|
129 | } |
---|
130 | |
---|
131 | public static bool NotifyIme(IntPtr hIMC) |
---|
132 | { |
---|
133 | return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL); |
---|
134 | } |
---|
135 | |
---|
136 | public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea) |
---|
137 | { |
---|
138 | if (textArea == null) |
---|
139 | throw new ArgumentNullException("textArea"); |
---|
140 | Rect textViewBounds = textArea.TextView.GetBounds(source); |
---|
141 | Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); |
---|
142 | CompositionForm form = new CompositionForm(); |
---|
143 | form.dwStyle = 0x0020; |
---|
144 | form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left); |
---|
145 | form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top); |
---|
146 | form.rcArea.left = (int)textViewBounds.Left; |
---|
147 | form.rcArea.top = (int)textViewBounds.Top; |
---|
148 | form.rcArea.right = (int)textViewBounds.Right; |
---|
149 | form.rcArea.bottom = (int)textViewBounds.Bottom; |
---|
150 | return ImmSetCompositionWindow(hIMC, ref form); |
---|
151 | } |
---|
152 | |
---|
153 | public static bool SetCompositionFont(HwndSource source, IntPtr hIMC, TextArea textArea) |
---|
154 | { |
---|
155 | if (textArea == null) |
---|
156 | throw new ArgumentNullException("textArea"); |
---|
157 | LOGFONT lf = new LOGFONT(); |
---|
158 | Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source); |
---|
159 | lf.lfFaceName = textArea.FontFamily.Source; |
---|
160 | lf.lfHeight = (int)characterBounds.Height; |
---|
161 | return ImmSetCompositionFont(hIMC, ref lf); |
---|
162 | } |
---|
163 | |
---|
164 | static Rect GetBounds(this TextView textView, HwndSource source) |
---|
165 | { |
---|
166 | // this may happen during layout changes in AvalonDock, so we just return an empty rectangle |
---|
167 | // in those cases. It should be refreshed immediately. |
---|
168 | if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) |
---|
169 | return EMPTY_RECT; |
---|
170 | Rect displayRect = new Rect(0, 0, textView.ActualWidth, textView.ActualHeight); |
---|
171 | return textView |
---|
172 | .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual |
---|
173 | .TransformToDevice(source.RootVisual); // rect on HWND |
---|
174 | } |
---|
175 | |
---|
176 | static readonly Rect EMPTY_RECT = new Rect(0, 0, 0, 0); |
---|
177 | |
---|
178 | static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source) |
---|
179 | { |
---|
180 | VisualLine vl = textView.GetVisualLine(pos.Line); |
---|
181 | if (vl == null) |
---|
182 | return EMPTY_RECT; |
---|
183 | // this may happen during layout changes in AvalonDock, so we just return an empty rectangle |
---|
184 | // in those cases. It should be refreshed immediately. |
---|
185 | if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView)) |
---|
186 | return EMPTY_RECT; |
---|
187 | TextLine line = vl.GetTextLine(pos.VisualColumn, pos.IsAtEndOfLine); |
---|
188 | Rect displayRect; |
---|
189 | // calculate the display rect for the current character |
---|
190 | if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) { |
---|
191 | displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle; |
---|
192 | displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop)); |
---|
193 | } else { |
---|
194 | // if we are in virtual space, we just use one wide-space as character width |
---|
195 | displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop), |
---|
196 | new Size(textView.WideSpaceWidth, textView.DefaultLineHeight)); |
---|
197 | } |
---|
198 | // adjust to current scrolling |
---|
199 | displayRect.Offset(-textView.ScrollOffset); |
---|
200 | return textView |
---|
201 | .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual |
---|
202 | .TransformToDevice(source.RootVisual); // rect on HWND |
---|
203 | } |
---|
204 | } |
---|
205 | |
---|
206 | [ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] |
---|
207 | interface ITfThreadMgr |
---|
208 | { |
---|
209 | void Activate(out int clientId); |
---|
210 | void Deactivate(); |
---|
211 | void CreateDocumentMgr(out IntPtr docMgr); |
---|
212 | void EnumDocumentMgrs(out IntPtr enumDocMgrs); |
---|
213 | void GetFocus(out IntPtr docMgr); |
---|
214 | void SetFocus(IntPtr docMgr); |
---|
215 | void AssociateFocus(IntPtr hwnd, IntPtr newDocMgr, out IntPtr prevDocMgr); |
---|
216 | void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus); |
---|
217 | void GetFunctionProvider(ref Guid classId, out IntPtr funcProvider); |
---|
218 | void EnumFunctionProviders(out IntPtr enumProviders); |
---|
219 | void GetGlobalCompartment(out IntPtr compartmentMgr); |
---|
220 | } |
---|
221 | } |
---|