Blender  V2.93
GHOST_ImeWin32.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
17  * All rights reserved.
18  *
19  * The Original Code is: some of this file.
20  */
21 
26 #ifdef WITH_INPUT_IME
27 
28 # include "GHOST_ImeWin32.h"
29 # include "GHOST_C-api.h"
30 # include "GHOST_WindowWin32.h"
31 # include "utfconv.h"
32 
33 GHOST_ImeWin32::GHOST_ImeWin32()
34  : is_composing_(false),
35  ime_status_(false),
36  input_language_id_(LANG_USER_DEFAULT),
37  system_caret_(false),
38  caret_rect_(-1, -1, 0, 0),
39  is_first(true),
40  is_enable(true)
41 {
42 }
43 
44 GHOST_ImeWin32::~GHOST_ImeWin32()
45 {
46 }
47 
48 bool GHOST_ImeWin32::SetInputLanguage()
49 {
56  HKL keyboard_layout = ::GetKeyboardLayout(0);
57  input_language_id_ = LOWORD(keyboard_layout);
58  ime_status_ = ::ImmIsIME(keyboard_layout);
59  return ime_status_;
60 }
61 
62 void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
63 {
76  if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
77  PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
78  if (!system_caret_) {
79  if (::CreateCaret(window_handle, NULL, 1, 1)) {
80  system_caret_ = true;
81  }
82  }
83  }
84  /* Restore the positions of the IME windows. */
85  UpdateImeWindow(window_handle);
86 }
87 
88 void GHOST_ImeWin32::SetImeWindowStyle(
89  HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
90 {
101  *handled = TRUE;
102  lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
103  ::DefWindowProc(window_handle, message, wparam, lparam);
104 }
105 
106 void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
107 {
108  /* Destroy the system caret if we have created for this IME input context. */
109  if (system_caret_) {
110  ::DestroyCaret();
111  system_caret_ = false;
112  }
113 }
114 
115 void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context)
116 {
117  int x = caret_rect_.m_l;
118  int y = caret_rect_.m_t;
119  const int kCaretMargin = 1;
132  CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {x, y}, {0, 0, 0, 0}};
133  ::ImmSetCandidateWindow(imm_context, &candidate_position);
134  if (system_caret_) {
135  switch (PRIMARYLANGID(input_language_id_)) {
136  case LANG_JAPANESE:
137  ::SetCaretPos(x, y + caret_rect_.getHeight());
138  break;
139  default:
140  ::SetCaretPos(x, y);
141  break;
142  }
143  }
144  if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
151  y += kCaretMargin;
152  }
159  CANDIDATEFORM exclude_rectangle = {
160  0, CFS_EXCLUDE, {x, y}, {x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight()}};
161  ::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
162 }
163 
164 void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
165 {
166  /* Just move the IME window attached to the given window. */
167  if (caret_rect_.m_l >= 0 && caret_rect_.m_t >= 0) {
168  HIMC imm_context = ::ImmGetContext(window_handle);
169  if (imm_context) {
170  MoveImeWindow(window_handle, imm_context);
171  ::ImmReleaseContext(window_handle, imm_context);
172  }
173  }
174 }
175 
176 void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
177 {
183  if (is_composing_) {
184  HIMC imm_context = ::ImmGetContext(window_handle);
185  if (imm_context) {
186  ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
187  ::ImmReleaseContext(window_handle, imm_context);
188  }
189  ResetComposition(window_handle);
190  }
191 }
192 
193 void GHOST_ImeWin32::CheckFirst(HWND window_handle)
194 {
195  if (is_first) {
196  this->EndIME(window_handle);
197  is_first = false;
198  }
199 }
200 
201 void GHOST_ImeWin32::ResetComposition(HWND window_handle)
202 {
203  /* Currently, just reset the composition status. */
204  is_composing_ = false;
205 }
206 
207 void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
208 {
214  if (is_composing_) {
215  ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
216  ResetComposition(window_handle);
217  }
218 }
219 
220 void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
221 {
232  int target_start = -1;
233  int target_end = -1;
234  switch (PRIMARYLANGID(input_language_id_)) {
235  case LANG_KOREAN:
236  if (lparam & CS_NOMOVECARET) {
237  target_start = 0;
238  target_end = 1;
239  }
240  break;
241  case LANG_CHINESE: {
242  int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0);
243  if (clause_size) {
244  static std::vector<unsigned long> clauses;
245  clause_size = clause_size / sizeof(clauses[0]);
246  clauses.resize(clause_size);
247  ImmGetCompositionStringW(
248  imm_context, GCS_COMPCLAUSE, &clauses[0], sizeof(clauses[0]) * clause_size);
249  if (composition->cursor_position == composition->ime_string.size()) {
250  target_start = clauses[clause_size - 2];
251  target_end = clauses[clause_size - 1];
252  }
253  else {
254  for (int i = 0; i < clause_size - 1; i++) {
255  if (clauses[i] == composition->cursor_position) {
256  target_start = clauses[i];
257  target_end = clauses[i + 1];
258  break;
259  }
260  }
261  }
262  }
263  else {
264  if (composition->cursor_position != -1) {
265  target_start = composition->cursor_position;
266  target_end = composition->ime_string.size();
267  }
268  }
269  break;
270  }
271  case LANG_JAPANESE:
272 
279  if (lparam & GCS_COMPATTR) {
280  int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, NULL, 0);
281  if (attribute_size > 0) {
282  char *attribute_data = new char[attribute_size];
283  if (attribute_data) {
284  ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size);
285  for (target_start = 0; target_start < attribute_size; ++target_start) {
286  if (IsTargetAttribute(attribute_data[target_start]))
287  break;
288  }
289  for (target_end = target_start; target_end < attribute_size; ++target_end) {
290  if (!IsTargetAttribute(attribute_data[target_end]))
291  break;
292  }
293  if (target_start == attribute_size) {
299  target_end = target_start;
300  target_start = 0;
301  }
302  if (target_start != -1 && target_start < attribute_size &&
303  attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED) {
304  composition->cursor_position = target_start;
305  }
306  }
307  delete[] attribute_data;
308  }
309  }
310  break;
311  }
312  composition->target_start = target_start;
313  composition->target_end = target_end;
314 }
315 
316 bool GHOST_ImeWin32::GetString(HIMC imm_context,
317  WPARAM lparam,
318  int type,
319  ImeComposition *composition)
320 {
321  bool result = false;
322  if (lparam & type) {
323  int string_size = ::ImmGetCompositionStringW(imm_context, type, NULL, 0);
324  if (string_size > 0) {
325  int string_length = string_size / sizeof(wchar_t);
326  wchar_t *string_data = new wchar_t[string_length + 1];
327  string_data[string_length] = '\0';
328  if (string_data) {
329  /* Fill the given ImeComposition object. */
330  ::ImmGetCompositionStringW(imm_context, type, string_data, string_size);
331  composition->string_type = type;
332  composition->ime_string = string_data;
333  result = true;
334  }
335  delete[] string_data;
336  }
337  }
338  return result;
339 }
340 
341 bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
342 {
343  bool result = false;
344  HIMC imm_context = ::ImmGetContext(window_handle);
345  if (imm_context) {
346  /* Copy the result string to the ImeComposition object. */
347  result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
352  composition->cursor_position = -1;
353  composition->target_start = -1;
354  composition->target_end = -1;
355  ::ImmReleaseContext(window_handle, imm_context);
356  }
357  return result;
358 }
359 
360 bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
361 {
362  bool result = false;
363  HIMC imm_context = ::ImmGetContext(window_handle);
364  if (imm_context) {
365  /* Copy the composition string to the ImeComposition object. */
366  result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
367 
368  /* Retrieve the cursor position in the IME composition. */
369  int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, NULL, 0);
370  composition->cursor_position = cursor_position;
371  composition->target_start = -1;
372  composition->target_end = -1;
373 
374  /* Retrieve the target selection and Update the ImeComposition object. */
375  GetCaret(imm_context, lparam, composition);
376 
377  /* Mark that there is an ongoing composition. */
378  is_composing_ = true;
379 
380  ::ImmReleaseContext(window_handle, imm_context);
381  }
382  return result;
383 }
384 
385 void GHOST_ImeWin32::EndIME(HWND window_handle)
386 {
394  if (!is_enable)
395  return;
396  is_enable = false;
397  CleanupComposition(window_handle);
398  ::ImmAssociateContextEx(window_handle, NULL, 0);
399  eventImeData.composite_len = 0;
400 }
401 
402 void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete)
403 {
404  if (is_enable && complete)
405  return;
406  is_enable = true;
413  ::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
414  /* Complete the ongoing composition and move the IME windows. */
415  HIMC imm_context = ::ImmGetContext(window_handle);
416  if (imm_context) {
417  if (complete) {
426  CompleteComposition(window_handle, imm_context);
427  }
433  if (caret_rect.m_l >= 0 && caret_rect.m_t >= 0) {
434  caret_rect_ = caret_rect;
435  MoveImeWindow(window_handle, imm_context);
436  }
437  ::ImmReleaseContext(window_handle, imm_context);
438  }
439 }
440 
441 static void convert_utf16_to_utf8_len(std::wstring s, int &len)
442 {
443  if (len >= 0 && len <= s.size())
444  len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1;
445  else
446  len = -1;
447 }
448 
449 static size_t updateUtf8Buf(ImeComposition &info)
450 {
451  size_t len = count_utf_8_from_16(info.ime_string.c_str());
452  info.utf8_buf.resize(len);
453  conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len);
454  convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
455  convert_utf16_to_utf8_len(info.ime_string, info.target_start);
456  convert_utf16_to_utf8_len(info.ime_string, info.target_end);
457  return len - 1;
458 }
459 
460 void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
461 {
462  int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
463  int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
464  /* convert wchar to utf8 */
465  if (res) {
466  eventImeData.result_len = (GHOST_TUserDataPtr)updateUtf8Buf(resultInfo);
467  eventImeData.result = &resultInfo.utf8_buf[0];
468  }
469  else {
470  eventImeData.result = 0;
471  eventImeData.result_len = 0;
472  }
473  if (comp) {
474  eventImeData.composite_len = (GHOST_TUserDataPtr)updateUtf8Buf(compInfo);
475  eventImeData.composite = &compInfo.utf8_buf[0];
476  eventImeData.cursor_position = compInfo.cursor_position;
477  eventImeData.target_start = compInfo.target_start;
478  eventImeData.target_end = compInfo.target_end;
479  }
480  else {
481  eventImeData.composite = 0;
482  eventImeData.composite_len = 0;
483  eventImeData.cursor_position = -1;
484  eventImeData.target_start = -1;
485  eventImeData.target_end = -1;
486  }
487 }
488 
489 #endif // WITH_INPUT_IME
GHOST C-API function and type declarations.
void * GHOST_TUserDataPtr
Definition: GHOST_Types.h:89
typedef UINT
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
GHOST_TInt32 m_l
Definition: GHOST_Rect.h:169
GHOST_TInt32 m_t
Definition: GHOST_Rect.h:171
size_t count_utf_8_from_16(const wchar_t *string16)
Definition: utfconv.c:23
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:127
uint len