17# define IMELANG_ENGLISH "en"
18# define IMELANG_CHINESE "zh"
19# define IMELANG_JAPANESE "ja"
20# define IMELANG_KOREAN "ko"
22GHOST_ImeWin32::GHOST_ImeWin32()
23 : is_composing_(
false),
24 language_(IMELANG_ENGLISH),
25 conversion_modes_(IME_CMODE_ALPHANUMERIC),
26 sentence_mode_(IME_SMODE_NONE),
28 caret_rect_(-1, -1, 0, 0),
34GHOST_ImeWin32::~GHOST_ImeWin32() {}
36void GHOST_ImeWin32::UpdateInputLanguage()
39 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
41 MAKELCID(LOWORD(::GetKeyboardLayout(0)), SORT_DEFAULT), locale, LOCALE_NAME_MAX_LENGTH, 0);
43 WCHAR language_u16[W32_ISO639_LEN];
44 GetLocaleInfoEx(locale, LOCALE_SISO639LANGNAME, language_u16, W32_ISO639_LEN);
47 CP_UTF8, 0, language_u16, W32_ISO639_LEN, language_, W32_ISO639_LEN,
nullptr,
nullptr);
50BOOL GHOST_ImeWin32::IsLanguage(
const char name[W32_ISO639_LEN])
52 return (strcmp(
name, language_) == 0);
55void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle)
57 HIMC imm_context = ::ImmGetContext(window_handle);
59 if (::ImmGetOpenStatus(imm_context)) {
60 ::ImmGetConversionStatus(imm_context, &conversion_modes_, &sentence_mode_);
63 conversion_modes_ = IME_CMODE_ALPHANUMERIC;
64 sentence_mode_ = IME_SMODE_NONE;
66 ::ImmReleaseContext(window_handle, imm_context);
69 conversion_modes_ = IME_CMODE_ALPHANUMERIC;
70 sentence_mode_ = IME_SMODE_NONE;
74bool GHOST_ImeWin32::IsEnglishMode()
76 return (conversion_modes_ & IME_CMODE_NOCONVERSION) ||
77 !(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
80bool GHOST_ImeWin32::IsImeKeyEvent(
char ascii,
GHOST_TKey key)
82 if (!(IsEnglishMode())) {
84 if ((ascii >=
'A' && ascii <=
'Z') || (ascii >=
'a' && ascii <=
'z')) {
87 if (IsLanguage(IMELANG_JAPANESE) && (ascii >=
' ' && ascii <=
'~')) {
90 if (IsLanguage(IMELANG_CHINESE)) {
94 if (conversion_modes_ & IME_CMODE_FULLSHAPE && (ascii >=
'0' && ascii <=
'9')) {
103void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
117 if (!system_caret_ && (IsLanguage(IMELANG_CHINESE) || IsLanguage(IMELANG_JAPANESE))) {
118 system_caret_ = ::CreateCaret(window_handle,
nullptr, 1, 1);
121 UpdateImeWindow(window_handle);
124void GHOST_ImeWin32::SetImeWindowStyle(
125 HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
138 lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
139 ::DefWindowProc(window_handle, message, wparam, lparam);
142void GHOST_ImeWin32::DestroyImeWindow(HWND )
147 system_caret_ =
false;
151void GHOST_ImeWin32::MoveImeWindow(HWND , HIMC imm_context)
153 int x = caret_rect_.l_;
154 int y = caret_rect_.t_;
155 const int kCaretMargin = 1;
168 CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {
x,
y}, {0, 0, 0, 0}};
169 ::ImmSetCandidateWindow(imm_context, &candidate_position);
173 if (IsLanguage(IMELANG_KOREAN)) {
188 CANDIDATEFORM exclude_rectangle = {
189 0, CFS_EXCLUDE, {
x,
y}, {
x,
y,
x + caret_rect_.getWidth(),
y + caret_rect_.getHeight()}};
190 ::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
193void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
196 if (caret_rect_.l_ >= 0 && caret_rect_.t_ >= 0) {
197 HIMC imm_context = ::ImmGetContext(window_handle);
199 MoveImeWindow(window_handle, imm_context);
200 ::ImmReleaseContext(window_handle, imm_context);
205void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
213 HIMC imm_context = ::ImmGetContext(window_handle);
215 ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
216 ::ImmReleaseContext(window_handle, imm_context);
218 ResetComposition(window_handle);
222void GHOST_ImeWin32::CheckFirst(HWND window_handle)
225 this->EndIME(window_handle);
230void GHOST_ImeWin32::ResetComposition(HWND )
233 is_composing_ =
false;
236void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
244 ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
245 ResetComposition(window_handle);
249void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
261 int target_start = -1;
263 if (IsLanguage(IMELANG_KOREAN)) {
264 if (lparam & CS_NOMOVECARET) {
269 else if (IsLanguage(IMELANG_CHINESE)) {
270 int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE,
nullptr, 0);
272 static std::vector<ulong> clauses;
273 clause_size = clause_size /
sizeof(clauses[0]);
274 clauses.resize(clause_size);
275 ImmGetCompositionStringW(
276 imm_context, GCS_COMPCLAUSE, &clauses[0],
sizeof(clauses[0]) * clause_size);
277 if (composition->cursor_position == composition->ime_string.size()) {
278 target_start = clauses[clause_size - 2];
279 target_end = clauses[clause_size - 1];
282 for (
int i = 0;
i < clause_size - 1;
i++) {
283 if (clauses[
i] == composition->cursor_position) {
284 target_start = clauses[
i];
285 target_end = clauses[
i + 1];
292 if (composition->cursor_position != -1) {
293 target_start = composition->cursor_position;
294 target_end = composition->ime_string.size();
298 else if (IsLanguage(IMELANG_JAPANESE)) {
305 if (lparam & GCS_COMPATTR) {
306 int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR,
nullptr, 0);
307 if (attribute_size > 0) {
308 char *attribute_data =
new char[attribute_size];
309 if (attribute_data) {
310 ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size);
311 for (target_start = 0; target_start < attribute_size; ++target_start) {
312 if (IsTargetAttribute(attribute_data[target_start])) {
316 for (target_end = target_start; target_end < attribute_size; ++target_end) {
317 if (!IsTargetAttribute(attribute_data[target_end])) {
321 if (target_start == attribute_size) {
327 target_end = target_start;
330 if (target_start != -1 && target_start < attribute_size &&
331 attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED)
333 composition->cursor_position = target_start;
336 delete[] attribute_data;
340 composition->target_start = target_start;
341 composition->target_end = target_end;
344bool GHOST_ImeWin32::GetString(HIMC imm_context,
347 ImeComposition *composition)
351 int string_size = ::ImmGetCompositionStringW(imm_context, type,
nullptr, 0);
352 if (string_size > 0) {
353 int string_length = string_size /
sizeof(wchar_t);
354 wchar_t *string_data =
new wchar_t[string_length + 1];
355 string_data[string_length] =
'\0';
358 ::ImmGetCompositionStringW(imm_context, type, string_data, string_size);
359 composition->string_type = type;
360 composition->ime_string = string_data;
363 delete[] string_data;
369bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
372 HIMC imm_context = ::ImmGetContext(window_handle);
375 result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
380 composition->cursor_position = -1;
381 composition->target_start = -1;
382 composition->target_end = -1;
383 ::ImmReleaseContext(window_handle, imm_context);
388bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
391 HIMC imm_context = ::ImmGetContext(window_handle);
394 result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
397 int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS,
nullptr, 0);
398 composition->cursor_position = cursor_position;
399 composition->target_start = -1;
400 composition->target_end = -1;
403 GetCaret(imm_context, lparam, composition);
406 is_composing_ =
true;
408 ::ImmReleaseContext(window_handle, imm_context);
413void GHOST_ImeWin32::EndIME(HWND window_handle)
427 HIMC imm_context = ::ImmGetContext(window_handle);
430 ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
431 ::ImmNotifyIME(imm_context, NI_CLOSECANDIDATE, 0, 0);
432 ::ImmReleaseContext(window_handle, imm_context);
434 ResetComposition(window_handle);
436 ::ImmAssociateContextEx(window_handle,
nullptr, 0);
437 eventImeData.composite.clear();
440void GHOST_ImeWin32::BeginIME(HWND window_handle,
const GHOST_Rect &caret_rect,
bool complete)
442 if (is_enable && complete) {
452 ::ImmAssociateContextEx(window_handle,
nullptr, IACE_DEFAULT);
454 HIMC imm_context = ::ImmGetContext(window_handle);
465 CompleteComposition(window_handle, imm_context);
472 if (caret_rect.
l_ >= 0 && caret_rect.
t_ >= 0) {
473 caret_rect_ = caret_rect;
474 MoveImeWindow(window_handle, imm_context);
476 ::ImmReleaseContext(window_handle, imm_context);
480static void convert_utf16_to_utf8_len(std::wstring s,
int &
len)
482 if (
len >= 0 &&
len <= s.size()) {
490static size_t updateUtf8Buf(ImeComposition &info)
493 info.utf8_buf.resize(
len);
495 convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
496 convert_utf16_to_utf8_len(info.ime_string, info.target_start);
497 convert_utf16_to_utf8_len(info.ime_string, info.target_end);
501void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
503 int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
504 int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
507 updateUtf8Buf(resultInfo);
508 eventImeData.result = std::string(&resultInfo.utf8_buf[0]);
511 eventImeData.result =
"";
514 updateUtf8Buf(compInfo);
515 eventImeData.composite = std::string(&compInfo.utf8_buf[0]);
516 eventImeData.cursor_position = compInfo.cursor_position;
517 eventImeData.target_start = compInfo.target_start;
518 eventImeData.target_end = compInfo.target_end;
521 eventImeData.composite =
"";
522 eventImeData.cursor_position = -1;
523 eventImeData.target_start = -1;
524 eventImeData.target_end = -1;
GHOST C-API function and type declarations.
size_t count_utf_8_from_16(const wchar_t *string16)
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)