OSDN Git Service

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