OSDN Git Service

Paddingプロパティを設定すると余白の部分が黒くなってしまう問題を修正した
[fooeditengine/FooEditEngine.git] / Windows / FooEditEngine / WinIME.cs
1 /*\r
2  * Copyright (C) 2013 FooProject\r
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by\r
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.\r
5 \r
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of \r
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
8 \r
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.\r
10  */\r
11 using System;\r
12 using System.Drawing;\r
13 using System.Windows.Forms;\r
14 using System.Runtime.InteropServices;\r
15 \r
16 using GDIP = System.Drawing;\r
17 \r
18 namespace FooEditEngine.Windows\r
19 {\r
20     internal class StartCompstionEventArgs : EventArgs\r
21     {\r
22     }\r
23 \r
24     internal class EndCompstionEventArgs : EventArgs\r
25     {\r
26     }\r
27 \r
28     internal class ImeCompstionEventArgs : EventArgs\r
29     {\r
30         /// <summary>\r
31         /// 確定時の文字列\r
32         /// </summary>\r
33         public string InputText;\r
34         public ImeCompstionEventArgs(string text)\r
35         {\r
36             this.InputText = text;\r
37         }\r
38     }\r
39 \r
40     internal class ImeDocumentFeedEventArgs : EventArgs\r
41     {\r
42         /// <summary>\r
43         /// 前後参照文脈変換につかう文字列\r
44         /// </summary>\r
45         public string Pragraph = string.Empty;\r
46         /// <summary>\r
47         /// IMEによって挿入される位置\r
48         /// </summary>\r
49         public int pos = 0;\r
50     }\r
51 \r
52     internal class ImeQueryRecovertStringEventArgs : EventArgs\r
53     {\r
54         /// <summary>\r
55         /// IMEによって調整された再変換文字列の開始位置\r
56         /// </summary>\r
57         public int offset;\r
58         /// <summary>\r
59         /// IMEによって調整された再変換文字列の長さ\r
60         /// </summary>\r
61         public int length;\r
62         public ImeQueryRecovertStringEventArgs(int offset, int length)\r
63         {\r
64             this.offset = offset;\r
65             this.length = length;\r
66         }\r
67     }\r
68 \r
69     internal class ImeReconvertStringEventArgs : EventArgs\r
70     {\r
71         /// <summary>\r
72         /// IMEに再変換の対象となる文字列を調整させる場合は真\r
73         /// </summary>\r
74         public bool AutoAdjust = false;\r
75         /// <summary>\r
76         /// 再変換の対象となる文字列\r
77         /// </summary>\r
78         public string TargetString = string.Empty;\r
79         /// <summary>\r
80         /// 変換中の文字列の座標\r
81         /// </summary>\r
82         public GDIP.Point CaretPostion = GDIP.Point.Empty;\r
83     }\r
84 \r
85     internal delegate void StartCompstionEventHandeler(object sender, StartCompstionEventArgs e);\r
86     internal delegate void EndCompstionEventHandeler(object sender, EndCompstionEventArgs e);\r
87     internal delegate void ImeCompstionEventHandeler(object sender, ImeCompstionEventArgs e);\r
88     internal delegate void ImeDocumentFeedEventHandler(object sender, ImeDocumentFeedEventArgs e);\r
89     internal delegate void ImeReconvertStringEventHandler(object sender, ImeReconvertStringEventArgs e);\r
90     internal delegate void ImeQueryReconvertStringEventHandler(object sender,ImeQueryRecovertStringEventArgs e);\r
91 \r
92     internal class WinIME : NativeWindow\r
93     {\r
94         [StructLayout(LayoutKind.Sequential)]\r
95         struct POINT\r
96         {\r
97             public POINT(int x, int y) { this.x = x; this.y = y; }\r
98             public POINT(GDIP.Point pt) { x = pt.X; y = pt.Y; }\r
99             public Int32 x, y;\r
100         }\r
101 \r
102         [StructLayout(LayoutKind.Sequential)]\r
103         struct RECT\r
104         {\r
105             public RECT(System.Drawing.Rectangle rect)\r
106             {\r
107                 left = rect.Left;\r
108                 top = rect.Top;\r
109                 right = rect.Right;\r
110                 bottom = rect.Bottom;\r
111             }\r
112             public Int32 left, top, right, bottom;\r
113         }\r
114 \r
115         [StructLayout(LayoutKind.Sequential)]\r
116         struct COMPOSITIONFORM\r
117         {\r
118             public UInt32 style;\r
119             public POINT currentPos;\r
120             public RECT area;\r
121         }\r
122 \r
123         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\r
124         struct RECONVERTSTRING\r
125         {\r
126             public UInt32 dwSize;\r
127             public UInt32 dwVersion;\r
128             public UInt32 dwStrLen;\r
129             public UInt32 dwStrOffset;\r
130             public UInt32 dwCompStrLen;\r
131             public UInt32 dwCompStrOffset;\r
132             public UInt32 dwTargetStrLen;\r
133             public UInt32 dwTargetStrOffset;\r
134         }\r
135 \r
136         [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]\r
137         class LOGFONT\r
138         {\r
139             public const int LF_FACESIZE = 32;\r
140             public int lfHeight;\r
141             public int lfWidth;\r
142             public int lfEscapement;\r
143             public int lfOrientation;\r
144             public int lfWeight;\r
145             public byte lfItalic;\r
146             public byte lfUnderline;\r
147             public byte lfStrikeOut;\r
148             public byte lfCharSet;\r
149             public byte lfOutPrecision;\r
150             public byte lfClipPrecision;\r
151             public byte lfQuality;\r
152             public byte lfPitchAndFamily;\r
153             [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]\r
154             public string lfFaceName;\r
155         }\r
156 \r
157         const Int32 GCS_COMPREADSTR = 0x0001;\r
158         const Int32 GCS_COMPSTR = 0x0008;\r
159         const Int32 GCS_RESULTSTR = 0x0800;\r
160         const Int32 SCS_SETSTR = (GCS_COMPREADSTR | GCS_COMPSTR);\r
161         const int WM_IME_STARTCOMPOSITION = 0x010D;\r
162         const int WM_IME_ENDCOMPOSITION = 0x010E;\r
163         const int WM_IME_COMPOSITION = 0x010F;\r
164         const int WM_IME_NOTIFY = 0x0282;\r
165         const int WM_IME_CHAR = 0x0286;\r
166         const int WM_IME_REQUEST = 0x0288;\r
167         const int CFS_POINT = 0x0002;\r
168         const int IMR_RECONVERTSTRING = 0x0004;\r
169         const int IMR_DOCUMENTFEED = 0x0007;\r
170         const int SCS_QUERYRECONVERTSTRING = 0x00020000;\r
171 \r
172         [DllImport("imm32.dll")]\r
173         static extern IntPtr ImmGetContext(IntPtr hWnd);\r
174         \r
175         [DllImport("imm32.dll")]\r
176         static extern Int32 ImmReleaseContext(IntPtr hWnd, IntPtr context);\r
177 \r
178         [DllImport("imm32.dll")]\r
179         static unsafe extern Int32 ImmGetCompositionStringW(IntPtr imContext, UInt32 index, void* out_string, UInt32 maxStringLen);\r
180 \r
181         [DllImport("imm32.dll")]\r
182         static unsafe extern Int32 ImmSetCompositionStringW(IntPtr imContext, UInt32 index, void* lpComp, UInt32 dwCompLen, void* lpRead, UInt32 readLen);\r
183 \r
184         [DllImport("imm32.dll")]\r
185         static unsafe extern Int32 ImmSetCompositionWindow(IntPtr imContext, COMPOSITIONFORM* compForm);\r
186 \r
187         [DllImport("imm32.dll")]\r
188         static unsafe extern Int32 ImmSetCompositionFontW (IntPtr hIMC,[In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lplf);\r
189 \r
190         [DllImport("imm32.dll")]\r
191         static extern UInt32 ImmGetProperty(IntPtr inputLocale, UInt32 index);\r
192 \r
193         [DllImport("user32.dll")]\r
194         static extern IntPtr GetKeyboardLayout(UInt32 threadID);\r
195 \r
196         public WinIME(Control ctrl)\r
197         {\r
198             if (ctrl.IsHandleCreated)\r
199                 this.AssignHandle(ctrl.Handle);\r
200             ctrl.HandleCreated += new EventHandler(ctrl_HandleCreated);\r
201             ctrl.HandleDestroyed += new EventHandler(ctrl_HandleDestroyed);\r
202             this.StartCompstion += new StartCompstionEventHandeler((s,e)=>{});\r
203             this.EndCompstion +=new EndCompstionEventHandeler((s,e)=>{});\r
204             this.ImeCompstion +=new ImeCompstionEventHandeler((s,e)=>{});\r
205             this.ImeDocumentFeed += new ImeDocumentFeedEventHandler((s,e)=>{});\r
206             this.ImeReconvert += new ImeReconvertStringEventHandler((s, e) => { });\r
207             this.ImeQueryReconvert += new ImeQueryReconvertStringEventHandler((s,e)=>{});\r
208         }\r
209 \r
210         public event StartCompstionEventHandeler StartCompstion;\r
211         public event EndCompstionEventHandeler EndCompstion;\r
212         public event ImeCompstionEventHandeler ImeCompstion;\r
213         public event ImeDocumentFeedEventHandler ImeDocumentFeed;\r
214         public event ImeReconvertStringEventHandler ImeReconvert;\r
215         public event ImeQueryReconvertStringEventHandler ImeQueryReconvert;\r
216 \r
217         /// <summary>\r
218         /// コンポジッションウィンドウの位置\r
219         /// </summary>\r
220         public GDIP.Point Location\r
221         {\r
222             get { throw new NotImplementedException(); }\r
223             set\r
224             {\r
225                 this.SetImeCompstionWindowPos(this.Handle, value.X,value.Y);\r
226             }\r
227         }\r
228 \r
229         /// <summary>\r
230         /// 変換時のフォント\r
231         /// </summary>\r
232         public Font Font\r
233         {\r
234             get { throw new NotImplementedException(); }\r
235             set\r
236             {\r
237                 SetImeWindowFont(this.Handle, value);\r
238             }\r
239         }\r
240 \r
241         protected override void WndProc(ref Message m)\r
242         {\r
243             switch (m.Msg)\r
244             {\r
245                 case WinIME.WM_IME_CHAR:\r
246                     m.Result = IntPtr.Zero;\r
247                     return;\r
248                 case WinIME.WM_IME_COMPOSITION:\r
249                     if ((m.LParam.ToInt32() & WinIME.GCS_RESULTSTR) != 0)\r
250                     {\r
251                         string text = GetImeCompstionString(this.Handle);\r
252                         this.ImeCompstion(this, new ImeCompstionEventArgs(text));\r
253                     }\r
254                     break;\r
255                 case WinIME.WM_IME_STARTCOMPOSITION:\r
256                     this.StartCompstion(this, new StartCompstionEventArgs());\r
257                     break;\r
258                 case WinIME.WM_IME_ENDCOMPOSITION:\r
259                     this.EndCompstion(this, new EndCompstionEventArgs());\r
260                     break;\r
261                 case WinIME.WM_IME_REQUEST:\r
262                     if ((int)m.WParam == WinIME.IMR_DOCUMENTFEED)\r
263                     {\r
264                         m.Result = HandleIMR_DocumnetFeed(m.LParam);\r
265                         return;\r
266                     }\r
267                     if ((int)m.WParam == WinIME.IMR_RECONVERTSTRING)\r
268                     {\r
269                         m.Result = HandleIMR_ReconvertString(m.LParam);\r
270                         return;\r
271                     }\r
272                     break;\r
273             }\r
274             base.WndProc(ref m);\r
275         }\r
276 \r
277         unsafe private IntPtr HandleIMR_ReconvertString(IntPtr lParam)\r
278         {\r
279             ImeReconvertStringEventArgs e = new ImeReconvertStringEventArgs();\r
280             this.ImeReconvert(this, e);\r
281             RECONVERTSTRING* reconv = (RECONVERTSTRING*)lParam.ToPointer();\r
282             char* paragraph = (char*)((byte*)reconv + sizeof(RECONVERTSTRING));\r
283             int reconvlen = sizeof(RECONVERTSTRING) + e.TargetString.Length * sizeof(char);\r
284             if (reconv != null)\r
285             {\r
286                 reconv->dwSize = (uint)sizeof(RECONVERTSTRING);\r
287                 reconv->dwVersion = 0;\r
288                 reconv->dwStrLen = (uint)e.TargetString.Length;\r
289                 reconv->dwStrOffset = (uint)sizeof(RECONVERTSTRING);\r
290                 reconv->dwTargetStrLen = 0;\r
291                 reconv->dwTargetStrOffset = 0;\r
292                 for (int i = 0; i < e.TargetString.Length; i++)\r
293                     paragraph[i] = e.TargetString[i];\r
294                 if (e.AutoAdjust)\r
295                 {\r
296                     IntPtr ime = ImmGetContext(this.Handle);\r
297                     ImmSetCompositionStringW(ime, SCS_QUERYRECONVERTSTRING, reconv, (uint)reconvlen, (void*)IntPtr.Zero, 0);\r
298                     ImmReleaseContext(this.Handle, ime);\r
299                     this.ImeQueryReconvert(this, new ImeQueryRecovertStringEventArgs((int)reconv->dwTargetStrOffset, (int)reconv->dwTargetStrLen));\r
300                 }\r
301                 else\r
302                 {\r
303                     reconv->dwCompStrLen = (uint)e.TargetString.Length;\r
304                     reconv->dwCompStrOffset = 0;\r
305                 }\r
306 \r
307                 this.Location = e.CaretPostion;\r
308             }\r
309             return new IntPtr(reconvlen);\r
310         }\r
311 \r
312         unsafe private IntPtr HandleIMR_DocumnetFeed(IntPtr lParam)\r
313         {\r
314             ImeDocumentFeedEventArgs e = new ImeDocumentFeedEventArgs();\r
315             this.ImeDocumentFeed(this, e);\r
316             if (lParam.ToInt32() != 0)\r
317             {\r
318                 if (e.pos > e.Pragraph.Length)\r
319                     e.pos = e.Pragraph.Length;\r
320                 else if (e.pos < 0)\r
321                     e.pos = 0;\r
322 \r
323                 RECONVERTSTRING* reconv = (RECONVERTSTRING*)lParam.ToPointer();\r
324                 char* paragraph = (char*)((byte*)reconv + sizeof(RECONVERTSTRING));\r
325                 reconv->dwSize = (uint)sizeof(RECONVERTSTRING);\r
326                 reconv->dwVersion = 0;\r
327                 reconv->dwStrLen = (uint)e.Pragraph.Length;\r
328                 reconv->dwStrOffset = (uint)sizeof(RECONVERTSTRING);\r
329                 reconv->dwCompStrLen = 0;\r
330                 reconv->dwCompStrOffset = 0;\r
331                 reconv->dwTargetStrLen = 0;\r
332                 reconv->dwTargetStrOffset = (uint)e.pos * sizeof(char);\r
333                 for (int i = 0; i < e.Pragraph.Length; i++)\r
334                     paragraph[i] = e.Pragraph[i];\r
335             }\r
336             return new IntPtr(sizeof(RECONVERTSTRING) + e.Pragraph.Length * sizeof(char));\r
337         }\r
338 \r
339         void ctrl_HandleCreated(object sender, EventArgs e)\r
340         {\r
341             Control ctrl = (Control)sender;\r
342             this.AssignHandle(ctrl.Handle);\r
343         }\r
344 \r
345         void ctrl_HandleDestroyed(object sender, EventArgs e)\r
346         {\r
347             this.ReleaseHandle();\r
348         }\r
349 \r
350         string GetImeCompstionString(IntPtr window)\r
351         {\r
352             string text;\r
353             unsafe\r
354             {\r
355                 IntPtr ime;\r
356                 int len;\r
357 \r
358                 ime = ImmGetContext(window);\r
359                 len = ImmGetCompositionStringW(ime, GCS_RESULTSTR, null, 0);\r
360                 fixed (char* buf = new char[len + 1])\r
361                 {\r
362                     ImmGetCompositionStringW(ime, GCS_RESULTSTR, (void*)buf, (uint)len);\r
363                     buf[len] = '\0';\r
364                     text = new String(buf);\r
365                 }\r
366                 ImmReleaseContext(window, ime);\r
367             }\r
368             return text;\r
369         }\r
370 \r
371         void SetImeCompstionWindowPos(IntPtr window, int x,int y)\r
372         {\r
373             IntPtr imContext;\r
374             imContext = ImmGetContext(window);\r
375 \r
376             COMPOSITIONFORM compForm = new COMPOSITIONFORM();\r
377             unsafe\r
378             {\r
379                 compForm.style = CFS_POINT;\r
380                 compForm.currentPos = new POINT(x, y);\r
381                 compForm.area = new RECT();\r
382 \r
383                 ImmSetCompositionWindow(imContext, &compForm);\r
384             }\r
385 \r
386             ImmReleaseContext(window, imContext);\r
387         }\r
388 \r
389         void SetImeWindowFont(IntPtr window, Font font)\r
390         {\r
391             IntPtr imContext;\r
392             LOGFONT logicalFont = new LOGFONT();\r
393 \r
394             font.ToLogFont(logicalFont);\r
395 \r
396             imContext = ImmGetContext(window);\r
397             unsafe\r
398             {\r
399                 ImmSetCompositionFontW(imContext, logicalFont);\r
400             }\r
401             ImmReleaseContext(window, imContext);\r
402         }\r
403     }\r
404 }\r