/*
* Copyright (C) 2013 FooProject
* * 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
* the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using GDIP = System.Drawing;
namespace FooEditEngine.Windows
{
internal class StartCompstionEventArgs : EventArgs
{
}
internal class EndCompstionEventArgs : EventArgs
{
}
internal class ImeCompstionEventArgs : EventArgs
{
///
/// 確定時の文字列
///
public string InputText;
public ImeCompstionEventArgs(string text)
{
this.InputText = text;
}
}
internal class ImeDocumentFeedEventArgs : EventArgs
{
///
/// 前後参照文脈変換につかう文字列
///
public string Pragraph = string.Empty;
///
/// IMEによって挿入される位置
///
public int pos = 0;
}
internal class ImeQueryRecovertStringEventArgs : EventArgs
{
///
/// IMEによって調整された再変換文字列の開始位置
///
public int offset;
///
/// IMEによって調整された再変換文字列の長さ
///
public int length;
public ImeQueryRecovertStringEventArgs(int offset, int length)
{
this.offset = offset;
this.length = length;
}
}
internal class ImeReconvertStringEventArgs : EventArgs
{
///
/// IMEに再変換の対象となる文字列を調整させる場合は真
///
public bool AutoAdjust = false;
///
/// 再変換の対象となる文字列
///
public string TargetString = string.Empty;
///
/// 変換中の文字列の座標
///
public GDIP.Point CaretPostion = GDIP.Point.Empty;
}
internal delegate void StartCompstionEventHandeler(object sender, StartCompstionEventArgs e);
internal delegate void EndCompstionEventHandeler(object sender, EndCompstionEventArgs e);
internal delegate void ImeCompstionEventHandeler(object sender, ImeCompstionEventArgs e);
internal delegate void ImeDocumentFeedEventHandler(object sender, ImeDocumentFeedEventArgs e);
internal delegate void ImeReconvertStringEventHandler(object sender, ImeReconvertStringEventArgs e);
internal delegate void ImeQueryReconvertStringEventHandler(object sender,ImeQueryRecovertStringEventArgs e);
internal class WinIME : NativeWindow
{
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public POINT(int x, int y) { this.x = x; this.y = y; }
public POINT(GDIP.Point pt) { x = pt.X; y = pt.Y; }
public Int32 x, y;
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public RECT(System.Drawing.Rectangle rect)
{
left = rect.Left;
top = rect.Top;
right = rect.Right;
bottom = rect.Bottom;
}
public Int32 left, top, right, bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct COMPOSITIONFORM
{
public UInt32 style;
public POINT currentPos;
public RECT area;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RECONVERTSTRING
{
public UInt32 dwSize;
public UInt32 dwVersion;
public UInt32 dwStrLen;
public UInt32 dwStrOffset;
public UInt32 dwCompStrLen;
public UInt32 dwCompStrOffset;
public UInt32 dwTargetStrLen;
public UInt32 dwTargetStrOffset;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
class LOGFONT
{
public const int LF_FACESIZE = 32;
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 = LF_FACESIZE)]
public string lfFaceName;
}
const Int32 GCS_COMPREADSTR = 0x0001;
const Int32 GCS_COMPSTR = 0x0008;
const Int32 GCS_RESULTSTR = 0x0800;
const Int32 SCS_SETSTR = (GCS_COMPREADSTR | GCS_COMPSTR);
const int WM_IME_STARTCOMPOSITION = 0x010D;
const int WM_IME_ENDCOMPOSITION = 0x010E;
const int WM_IME_COMPOSITION = 0x010F;
const int WM_IME_NOTIFY = 0x0282;
const int WM_IME_CHAR = 0x0286;
const int WM_IME_REQUEST = 0x0288;
const int CFS_POINT = 0x0002;
const int IMR_RECONVERTSTRING = 0x0004;
const int IMR_DOCUMENTFEED = 0x0007;
const int SCS_QUERYRECONVERTSTRING = 0x00020000;
[DllImport("imm32.dll")]
static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("imm32.dll")]
static extern Int32 ImmReleaseContext(IntPtr hWnd, IntPtr context);
[DllImport("imm32.dll")]
static unsafe extern Int32 ImmGetCompositionStringW(IntPtr imContext, UInt32 index, void* out_string, UInt32 maxStringLen);
[DllImport("imm32.dll")]
static unsafe extern Int32 ImmSetCompositionStringW(IntPtr imContext, UInt32 index, void* lpComp, UInt32 dwCompLen, void* lpRead, UInt32 readLen);
[DllImport("imm32.dll")]
static unsafe extern Int32 ImmSetCompositionWindow(IntPtr imContext, COMPOSITIONFORM* compForm);
[DllImport("imm32.dll")]
static unsafe extern Int32 ImmSetCompositionFontW (IntPtr hIMC,[In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lplf);
[DllImport("imm32.dll")]
static extern UInt32 ImmGetProperty(IntPtr inputLocale, UInt32 index);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(UInt32 threadID);
public WinIME(Control ctrl)
{
if (ctrl.IsHandleCreated)
this.AssignHandle(ctrl.Handle);
ctrl.HandleCreated += new EventHandler(ctrl_HandleCreated);
ctrl.HandleDestroyed += new EventHandler(ctrl_HandleDestroyed);
this.StartCompstion += new StartCompstionEventHandeler((s,e)=>{});
this.EndCompstion +=new EndCompstionEventHandeler((s,e)=>{});
this.ImeCompstion +=new ImeCompstionEventHandeler((s,e)=>{});
this.ImeDocumentFeed += new ImeDocumentFeedEventHandler((s,e)=>{});
this.ImeReconvert += new ImeReconvertStringEventHandler((s, e) => { });
this.ImeQueryReconvert += new ImeQueryReconvertStringEventHandler((s,e)=>{});
}
public event StartCompstionEventHandeler StartCompstion;
public event EndCompstionEventHandeler EndCompstion;
public event ImeCompstionEventHandeler ImeCompstion;
public event ImeDocumentFeedEventHandler ImeDocumentFeed;
public event ImeReconvertStringEventHandler ImeReconvert;
public event ImeQueryReconvertStringEventHandler ImeQueryReconvert;
///
/// コンポジッションウィンドウの位置
///
public GDIP.Point Location
{
get { throw new NotImplementedException(); }
set
{
this.SetImeCompstionWindowPos(this.Handle, value.X,value.Y);
}
}
///
/// 変換時のフォント
///
public Font Font
{
get { throw new NotImplementedException(); }
set
{
SetImeWindowFont(this.Handle, value);
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WinIME.WM_IME_CHAR:
m.Result = IntPtr.Zero;
return;
case WinIME.WM_IME_COMPOSITION:
if ((m.LParam.ToInt32() & WinIME.GCS_RESULTSTR) != 0)
{
string text = GetImeCompstionString(this.Handle);
this.ImeCompstion(this, new ImeCompstionEventArgs(text));
}
break;
case WinIME.WM_IME_STARTCOMPOSITION:
this.StartCompstion(this, new StartCompstionEventArgs());
break;
case WinIME.WM_IME_ENDCOMPOSITION:
this.EndCompstion(this, new EndCompstionEventArgs());
break;
case WinIME.WM_IME_REQUEST:
if ((int)m.WParam == WinIME.IMR_DOCUMENTFEED)
{
m.Result = HandleIMR_DocumnetFeed(m.LParam);
return;
}
if ((int)m.WParam == WinIME.IMR_RECONVERTSTRING)
{
m.Result = HandleIMR_ReconvertString(m.LParam);
return;
}
break;
}
base.WndProc(ref m);
}
unsafe private IntPtr HandleIMR_ReconvertString(IntPtr lParam)
{
ImeReconvertStringEventArgs e = new ImeReconvertStringEventArgs();
this.ImeReconvert(this, e);
RECONVERTSTRING* reconv = (RECONVERTSTRING*)lParam.ToPointer();
char* paragraph = (char*)((byte*)reconv + sizeof(RECONVERTSTRING));
int reconvlen = sizeof(RECONVERTSTRING) + e.TargetString.Length * sizeof(char);
if (reconv != null)
{
reconv->dwSize = (uint)sizeof(RECONVERTSTRING);
reconv->dwVersion = 0;
reconv->dwStrLen = (uint)e.TargetString.Length;
reconv->dwStrOffset = (uint)sizeof(RECONVERTSTRING);
reconv->dwTargetStrLen = 0;
reconv->dwTargetStrOffset = 0;
for (int i = 0; i < e.TargetString.Length; i++)
paragraph[i] = e.TargetString[i];
if (e.AutoAdjust)
{
IntPtr ime = ImmGetContext(this.Handle);
ImmSetCompositionStringW(ime, SCS_QUERYRECONVERTSTRING, reconv, (uint)reconvlen, (void*)IntPtr.Zero, 0);
ImmReleaseContext(this.Handle, ime);
this.ImeQueryReconvert(this, new ImeQueryRecovertStringEventArgs((int)reconv->dwTargetStrOffset, (int)reconv->dwTargetStrLen));
}
else
{
reconv->dwCompStrLen = (uint)e.TargetString.Length;
reconv->dwCompStrOffset = 0;
}
this.Location = e.CaretPostion;
}
return new IntPtr(reconvlen);
}
unsafe private IntPtr HandleIMR_DocumnetFeed(IntPtr lParam)
{
ImeDocumentFeedEventArgs e = new ImeDocumentFeedEventArgs();
this.ImeDocumentFeed(this, e);
if (lParam.ToInt32() != 0)
{
if (e.pos > e.Pragraph.Length)
e.pos = e.Pragraph.Length;
else if (e.pos < 0)
e.pos = 0;
RECONVERTSTRING* reconv = (RECONVERTSTRING*)lParam.ToPointer();
char* paragraph = (char*)((byte*)reconv + sizeof(RECONVERTSTRING));
reconv->dwSize = (uint)sizeof(RECONVERTSTRING);
reconv->dwVersion = 0;
reconv->dwStrLen = (uint)e.Pragraph.Length;
reconv->dwStrOffset = (uint)sizeof(RECONVERTSTRING);
reconv->dwCompStrLen = 0;
reconv->dwCompStrOffset = 0;
reconv->dwTargetStrLen = 0;
reconv->dwTargetStrOffset = (uint)e.pos * sizeof(char);
for (int i = 0; i < e.Pragraph.Length; i++)
paragraph[i] = e.Pragraph[i];
}
return new IntPtr(sizeof(RECONVERTSTRING) + e.Pragraph.Length * sizeof(char));
}
void ctrl_HandleCreated(object sender, EventArgs e)
{
Control ctrl = (Control)sender;
this.AssignHandle(ctrl.Handle);
}
void ctrl_HandleDestroyed(object sender, EventArgs e)
{
this.ReleaseHandle();
}
string GetImeCompstionString(IntPtr window)
{
string text;
unsafe
{
IntPtr ime;
int len;
ime = ImmGetContext(window);
len = ImmGetCompositionStringW(ime, GCS_RESULTSTR, null, 0);
fixed (char* buf = new char[len + 1])
{
ImmGetCompositionStringW(ime, GCS_RESULTSTR, (void*)buf, (uint)len);
buf[len] = '\0';
text = new String(buf);
}
ImmReleaseContext(window, ime);
}
return text;
}
void SetImeCompstionWindowPos(IntPtr window, int x,int y)
{
IntPtr imContext;
imContext = ImmGetContext(window);
COMPOSITIONFORM compForm = new COMPOSITIONFORM();
unsafe
{
compForm.style = CFS_POINT;
compForm.currentPos = new POINT(x, y);
compForm.area = new RECT();
ImmSetCompositionWindow(imContext, &compForm);
}
ImmReleaseContext(window, imContext);
}
void SetImeWindowFont(IntPtr window, Font font)
{
IntPtr imContext;
LOGFONT logicalFont = new LOGFONT();
font.ToLogFont(logicalFont);
imContext = ImmGetContext(window);
unsafe
{
ImmSetCompositionFontW(imContext, logicalFont);
}
ImmReleaseContext(window, imContext);
}
}
}