OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ime_input.h" | 5 #include "ui/base/win/ime_input.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/scoped_ptr.h" | 8 #include "base/scoped_ptr.h" |
| 9 #include "base/string16.h" |
9 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" |
10 #include "third_party/skia/include/core/SkColor.h" | 12 #include "third_party/skia/include/core/SkColor.h" |
| 13 #include "ui/base/ime/composition_text.h" |
11 | 14 |
12 // "imm32.lib" is required by IMM32 APIs used in this file. | 15 // "imm32.lib" is required by IMM32 APIs used in this file. |
13 // NOTE(hbono): To comply with a comment from Darin, I have added | 16 // NOTE(hbono): To comply with a comment from Darin, I have added |
14 // this #pragma directive instead of adding "imm32.lib" to a project file. | 17 // this #pragma directive instead of adding "imm32.lib" to a project file. |
15 #pragma comment(lib, "imm32.lib") | 18 #pragma comment(lib, "imm32.lib") |
16 | 19 |
| 20 // Following code requires wchar_t to be same as char16. It should always be |
| 21 // true on Windows. |
| 22 COMPILE_ASSERT(sizeof(wchar_t) == sizeof(char16), wchar_t__char16_diff); |
| 23 |
17 /////////////////////////////////////////////////////////////////////////////// | 24 /////////////////////////////////////////////////////////////////////////////// |
18 // ImeInput | 25 // ImeInput |
19 | 26 |
20 namespace { | 27 namespace { |
21 | 28 |
22 // Determines whether or not the given attribute represents a target | 29 // Determines whether or not the given attribute represents a target |
23 // (a.k.a. a selection). | 30 // (a.k.a. a selection). |
24 bool IsTargetAttribute(char attribute) { | 31 bool IsTargetAttribute(char attribute) { |
25 return (attribute == ATTR_TARGET_CONVERTED || | 32 return (attribute == ATTR_TARGET_CONVERTED || |
26 attribute == ATTR_TARGET_NOTCONVERTED); | 33 attribute == ATTR_TARGET_NOTCONVERTED); |
(...skipping 28 matching lines...) Expand all Loading... |
55 end = attribute_size; | 62 end = attribute_size; |
56 } | 63 } |
57 } | 64 } |
58 *target_start = start; | 65 *target_start = start; |
59 *target_end = end; | 66 *target_end = end; |
60 } | 67 } |
61 } | 68 } |
62 | 69 |
63 // Helper function for ImeInput::GetCompositionInfo() method, to get underlines | 70 // Helper function for ImeInput::GetCompositionInfo() method, to get underlines |
64 // information of the current composition string. | 71 // information of the current composition string. |
65 void GetCompositionUnderlines( | 72 void GetCompositionUnderlines(HIMC imm_context, |
66 HIMC imm_context, | 73 int target_start, |
67 int target_start, | 74 int target_end, |
68 int target_end, | 75 ui::CompositionUnderlines* underlines) { |
69 std::vector<WebKit::WebCompositionUnderline>* underlines) { | |
70 int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, | 76 int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, |
71 NULL, 0); | 77 NULL, 0); |
72 int clause_length = clause_size / sizeof(uint32); | 78 int clause_length = clause_size / sizeof(uint32); |
73 if (clause_length) { | 79 if (clause_length) { |
74 scoped_array<uint32> clause_data(new uint32[clause_length]); | 80 scoped_array<uint32> clause_data(new uint32[clause_length]); |
75 if (clause_data.get()) { | 81 if (clause_data.get()) { |
76 ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, | 82 ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, |
77 clause_data.get(), clause_size); | 83 clause_data.get(), clause_size); |
78 for (int i = 0; i < clause_length - 1; ++i) { | 84 for (int i = 0; i < clause_length - 1; ++i) { |
79 WebKit::WebCompositionUnderline underline; | 85 ui::CompositionUnderline underline; |
80 underline.startOffset = clause_data[i]; | 86 underline.start_offset = clause_data[i]; |
81 underline.endOffset = clause_data[i+1]; | 87 underline.end_offset = clause_data[i+1]; |
82 underline.color = SK_ColorBLACK; | 88 underline.color = SK_ColorBLACK; |
83 underline.thick = false; | 89 underline.thick = false; |
84 | 90 |
85 // Use thick underline for the target clause. | 91 // Use thick underline for the target clause. |
86 if (underline.startOffset >= static_cast<unsigned>(target_start) && | 92 if (underline.start_offset >= static_cast<unsigned>(target_start) && |
87 underline.endOffset <= static_cast<unsigned>(target_end)) { | 93 underline.end_offset <= static_cast<unsigned>(target_end)) { |
88 underline.thick = true; | 94 underline.thick = true; |
89 } | 95 } |
90 underlines->push_back(underline); | 96 underlines->push_back(underline); |
91 } | 97 } |
92 } | 98 } |
93 } | 99 } |
94 } | 100 } |
95 | 101 |
| 102 // Checks if a given primary language ID is a RTL language. |
| 103 bool IsRTLPrimaryLangID(LANGID lang) { |
| 104 switch (lang) { |
| 105 case LANG_ARABIC: |
| 106 case LANG_HEBREW: |
| 107 case LANG_PERSIAN: |
| 108 case LANG_SYRIAC: |
| 109 case LANG_UIGHUR: |
| 110 case LANG_URDU: |
| 111 return true; |
| 112 default: |
| 113 return false; |
| 114 } |
| 115 } |
| 116 |
96 } // namespace | 117 } // namespace |
97 | 118 |
| 119 namespace ui { |
| 120 |
98 ImeInput::ImeInput() | 121 ImeInput::ImeInput() |
99 : ime_status_(false), | 122 : ime_status_(false), |
100 input_language_id_(LANG_USER_DEFAULT), | 123 input_language_id_(LANG_USER_DEFAULT), |
101 is_composing_(false), | 124 is_composing_(false), |
102 system_caret_(false), | 125 system_caret_(false), |
103 caret_rect_(-1, -1, 0, 0) { | 126 caret_rect_(-1, -1, 0, 0) { |
104 } | 127 } |
105 | 128 |
106 ImeInput::~ImeInput() { | 129 ImeInput::~ImeInput() { |
107 } | 130 } |
108 | 131 |
109 bool ImeInput::SetInputLanguage() { | 132 bool ImeInput::SetInputLanguage() { |
110 // Retrieve the current keyboard layout from Windows and determine whether | 133 // Retrieve the current keyboard layout from Windows and determine whether |
111 // or not the current input context has IMEs. | 134 // or not the current input context has IMEs. |
112 // Also save its input language for language-specific operations required | 135 // Also save its input language for language-specific operations required |
113 // while composing a text. | 136 // while composing a text. |
114 HKL keyboard_layout = ::GetKeyboardLayout(0); | 137 HKL keyboard_layout = ::GetKeyboardLayout(0); |
115 input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout); | 138 input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout); |
116 ime_status_ = (::ImmIsIME(keyboard_layout) == TRUE) ? true : false; | 139 ime_status_ = (::ImmIsIME(keyboard_layout) == TRUE) ? true : false; |
117 return ime_status_; | 140 return ime_status_; |
118 } | 141 } |
119 | 142 |
120 | |
121 void ImeInput::CreateImeWindow(HWND window_handle) { | 143 void ImeInput::CreateImeWindow(HWND window_handle) { |
122 // When a user disables TSF (Text Service Framework) and CUAS (Cicero | 144 // When a user disables TSF (Text Service Framework) and CUAS (Cicero |
123 // Unaware Application Support), Chinese IMEs somehow ignore function calls | 145 // Unaware Application Support), Chinese IMEs somehow ignore function calls |
124 // to ::ImmSetCandidateWindow(), i.e. they do not move their candidate | 146 // to ::ImmSetCandidateWindow(), i.e. they do not move their candidate |
125 // window to the position given as its parameters, and use the position | 147 // window to the position given as its parameters, and use the position |
126 // of the current system caret instead, i.e. it uses ::GetCaretPos() to | 148 // of the current system caret instead, i.e. it uses ::GetCaretPos() to |
127 // retrieve the position of their IME candidate window. | 149 // retrieve the position of their IME candidate window. |
128 // Therefore, we create a temporary system caret for Chinese IMEs and use | 150 // Therefore, we create a temporary system caret for Chinese IMEs and use |
129 // it during this input context. | 151 // it during this input context. |
130 // Since some third-party Japanese IME also uses ::GetCaretPos() to determine | 152 // Since some third-party Japanese IME also uses ::GetCaretPos() to determine |
131 // their window position, we also create a caret for Japanese IMEs. | 153 // their window position, we also create a caret for Japanese IMEs. |
132 if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE || | 154 if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE || |
133 PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) { | 155 PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) { |
134 if (!system_caret_) { | 156 if (!system_caret_) { |
135 if (::CreateCaret(window_handle, NULL, 1, 1)) { | 157 if (::CreateCaret(window_handle, NULL, 1, 1)) { |
136 system_caret_ = true; | 158 system_caret_ = true; |
137 } | 159 } |
138 } | 160 } |
139 } | 161 } |
140 // Restore the positions of the IME windows. | 162 // Restore the positions of the IME windows. |
141 UpdateImeWindow(window_handle); | 163 UpdateImeWindow(window_handle); |
142 } | 164 } |
143 | 165 |
144 void ImeInput::SetImeWindowStyle(HWND window_handle, UINT message, | 166 LRESULT ImeInput::SetImeWindowStyle(HWND window_handle, UINT message, |
145 WPARAM wparam, LPARAM lparam, | 167 WPARAM wparam, LPARAM lparam, |
146 BOOL* handled) { | 168 BOOL* handled) { |
147 // To prevent the IMM (Input Method Manager) from displaying the IME | 169 // To prevent the IMM (Input Method Manager) from displaying the IME |
148 // composition window, Update the styles of the IME windows and EXPLICITLY | 170 // composition window, Update the styles of the IME windows and EXPLICITLY |
149 // call ::DefWindowProc() here. | 171 // call ::DefWindowProc() here. |
150 // NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update | 172 // NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update |
151 // the styles of IME windows because the 'lparam' variable is a local one | 173 // the styles of IME windows because the 'lparam' variable is a local one |
152 // and all its updates disappear in returning from this function, i.e. WTL | 174 // and all its updates disappear in returning from this function, i.e. WTL |
153 // does not call ::DefWindowProc() with our updated 'lparam' value but call | 175 // does not call ::DefWindowProc() with our updated 'lparam' value but call |
154 // the function with its original value and over-writes our window styles. | 176 // the function with its original value and over-writes our window styles. |
155 *handled = TRUE; | 177 *handled = TRUE; |
156 lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW; | 178 lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW; |
157 ::DefWindowProc(window_handle, message, wparam, lparam); | 179 return ::DefWindowProc(window_handle, message, wparam, lparam); |
158 } | 180 } |
159 | 181 |
160 void ImeInput::DestroyImeWindow(HWND window_handle) { | 182 void ImeInput::DestroyImeWindow(HWND window_handle) { |
161 // Destroy the system caret if we have created for this IME input context. | 183 // Destroy the system caret if we have created for this IME input context. |
162 if (system_caret_) { | 184 if (system_caret_) { |
163 ::DestroyCaret(); | 185 ::DestroyCaret(); |
164 system_caret_ = false; | 186 system_caret_ = false; |
165 } | 187 } |
166 } | 188 } |
167 | 189 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 // We have to confirm there is an ongoing composition before completing it. | 264 // We have to confirm there is an ongoing composition before completing it. |
243 // This is for preventing some IMEs from getting confused while completing an | 265 // This is for preventing some IMEs from getting confused while completing an |
244 // ongoing composition even if they do not have any ongoing compositions.) | 266 // ongoing composition even if they do not have any ongoing compositions.) |
245 if (is_composing_) { | 267 if (is_composing_) { |
246 ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); | 268 ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); |
247 ResetComposition(window_handle); | 269 ResetComposition(window_handle); |
248 } | 270 } |
249 } | 271 } |
250 | 272 |
251 void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam, | 273 void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam, |
252 ImeComposition* composition) { | 274 CompositionText* composition) { |
253 // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and | 275 // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and |
254 // convert them into underlines and selection range respectively. | 276 // convert them into underlines and selection range respectively. |
255 composition->underlines.clear(); | 277 composition->underlines.clear(); |
256 | 278 |
257 int length = static_cast<int>(composition->ime_string.length()); | 279 int length = static_cast<int>(composition->text.length()); |
| 280 |
| 281 // Find out the range selected by the user. |
| 282 int target_start = length; |
| 283 int target_end = length; |
| 284 if (lparam & GCS_COMPATTR) |
| 285 GetCompositionTargetRange(imm_context, &target_start, &target_end); |
258 | 286 |
259 // Retrieve the selection range information. If CS_NOMOVECARET is specified, | 287 // Retrieve the selection range information. If CS_NOMOVECARET is specified, |
260 // that means the cursor should not be moved, then we just place the caret at | 288 // that means the cursor should not be moved, then we just place the caret at |
261 // the beginning of the composition string. Otherwise we should honour the | 289 // the beginning of the composition string. Otherwise we should honour the |
262 // GCS_CURSORPOS value if it's available. | 290 // GCS_CURSORPOS value if it's available. |
263 // TODO(suzhe): due to a bug of webkit, we currently can't use selection range | 291 // TODO(suzhe): due to a bug of webkit, we currently can't use selection range |
264 // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 | 292 // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 |
265 if (lparam & CS_NOMOVECARET) { | 293 if (lparam & CS_NOMOVECARET) { |
266 composition->selection_start = composition->selection_end = 0; | 294 composition->selection = ui::Range(0); |
267 } else if (lparam & GCS_CURSORPOS) { | 295 } else if (lparam & GCS_CURSORPOS) { |
268 composition->selection_start = composition->selection_end = | 296 // If cursor position is same as target_start or target_end, then selects |
269 ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0); | 297 // the target range instead. We always use cursor position as selection end, |
| 298 // so that if the client doesn't support drawing selection with composition, |
| 299 // it can always retrieve the correct cursor position. |
| 300 int cursor = ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0); |
| 301 if (cursor == target_start) |
| 302 composition->selection = ui::Range(target_end, cursor); |
| 303 else if (cursor == target_end) |
| 304 composition->selection = ui::Range(target_start, cursor); |
| 305 else |
| 306 composition->selection = ui::Range(cursor); |
270 } else { | 307 } else { |
271 composition->selection_start = composition->selection_end = length; | 308 composition->selection = ui::Range(target_start, target_end); |
272 } | 309 } |
273 | 310 |
274 // Find out the range selected by the user. | |
275 int target_start = 0; | |
276 int target_end = 0; | |
277 if (lparam & GCS_COMPATTR) | |
278 GetCompositionTargetRange(imm_context, &target_start, &target_end); | |
279 | |
280 // Retrieve the clause segmentations and convert them to underlines. | 311 // Retrieve the clause segmentations and convert them to underlines. |
281 if (lparam & GCS_COMPCLAUSE) { | 312 if (lparam & GCS_COMPCLAUSE) { |
282 GetCompositionUnderlines(imm_context, target_start, target_end, | 313 GetCompositionUnderlines(imm_context, target_start, target_end, |
283 &composition->underlines); | 314 &composition->underlines); |
284 } | 315 } |
285 | 316 |
286 // Set default underlines in case there is no clause information. | 317 // Set default underlines in case there is no clause information. |
287 if (!composition->underlines.size()) { | 318 if (!composition->underlines.size()) { |
288 WebKit::WebCompositionUnderline underline; | 319 CompositionUnderline underline; |
289 underline.color = SK_ColorBLACK; | 320 underline.color = SK_ColorBLACK; |
290 if (target_start > 0) { | 321 if (target_start > 0) { |
291 underline.startOffset = 0; | 322 underline.start_offset = 0; |
292 underline.endOffset = target_start; | 323 underline.end_offset = target_start; |
293 underline.thick = false; | 324 underline.thick = false; |
294 composition->underlines.push_back(underline); | 325 composition->underlines.push_back(underline); |
295 } | 326 } |
296 if (target_end > target_start) { | 327 if (target_end > target_start) { |
297 underline.startOffset = target_start; | 328 underline.start_offset = target_start; |
298 underline.endOffset = target_end; | 329 underline.end_offset = target_end; |
299 underline.thick = true; | 330 underline.thick = true; |
300 composition->underlines.push_back(underline); | 331 composition->underlines.push_back(underline); |
301 } | 332 } |
302 if (target_end < length) { | 333 if (target_end < length) { |
303 underline.startOffset = target_end; | 334 underline.start_offset = target_end; |
304 underline.endOffset = length; | 335 underline.end_offset = length; |
305 underline.thick = false; | 336 underline.thick = false; |
306 composition->underlines.push_back(underline); | 337 composition->underlines.push_back(underline); |
307 } | 338 } |
308 } | 339 } |
309 } | 340 } |
310 | 341 |
311 bool ImeInput::GetString(HIMC imm_context, WPARAM lparam, int type, | 342 bool ImeInput::GetString(HIMC imm_context, WPARAM lparam, int type, |
312 ImeComposition* composition) { | 343 string16* result) { |
313 bool result = false; | 344 bool ret = false; |
314 if (lparam & type) { | 345 if (lparam & type) { |
315 int string_size = ::ImmGetCompositionString(imm_context, type, NULL, 0); | 346 int string_size = ::ImmGetCompositionString(imm_context, type, NULL, 0); |
316 if (string_size > 0) { | 347 if (string_size > 0) { |
317 int string_length = string_size / sizeof(wchar_t); | 348 int string_length = string_size / sizeof(wchar_t); |
318 wchar_t *string_data = WriteInto(&composition->ime_string, | 349 wchar_t *string_data = WriteInto(result, string_length + 1); |
319 string_length + 1); | |
320 if (string_data) { | 350 if (string_data) { |
321 // Fill the given ImeComposition object. | 351 // Fill the given result object. |
322 ::ImmGetCompositionString(imm_context, type, | 352 ::ImmGetCompositionString(imm_context, type, string_data, string_size); |
323 string_data, string_size); | 353 ret = true; |
324 composition->string_type = type; | |
325 result = true; | |
326 } | 354 } |
327 } | 355 } |
328 } | 356 } |
329 return result; | 357 return ret; |
330 } | 358 } |
331 | 359 |
332 bool ImeInput::GetResult(HWND window_handle, LPARAM lparam, | 360 bool ImeInput::GetResult(HWND window_handle, LPARAM lparam, string16* result) { |
333 ImeComposition* composition) { | 361 bool ret = false; |
334 bool result = false; | |
335 HIMC imm_context = ::ImmGetContext(window_handle); | 362 HIMC imm_context = ::ImmGetContext(window_handle); |
336 if (imm_context) { | 363 if (imm_context) { |
337 // Copy the result string to the ImeComposition object. | 364 ret = GetString(imm_context, lparam, GCS_RESULTSTR, result); |
338 result = GetString(imm_context, lparam, GCS_RESULTSTR, composition); | |
339 // Reset all the other parameters because a result string does not | |
340 // have composition attributes. | |
341 composition->selection_start = 0; | |
342 composition->selection_end = 0; | |
343 ::ImmReleaseContext(window_handle, imm_context); | 365 ::ImmReleaseContext(window_handle, imm_context); |
344 } | 366 } |
345 return result; | 367 return ret; |
346 } | 368 } |
347 | 369 |
348 bool ImeInput::GetComposition(HWND window_handle, LPARAM lparam, | 370 bool ImeInput::GetComposition(HWND window_handle, LPARAM lparam, |
349 ImeComposition* composition) { | 371 CompositionText* composition) { |
350 bool result = false; | 372 bool ret = false; |
351 HIMC imm_context = ::ImmGetContext(window_handle); | 373 HIMC imm_context = ::ImmGetContext(window_handle); |
352 if (imm_context) { | 374 if (imm_context) { |
353 // Copy the composition string to the ImeComposition object. | 375 // Copy the composition string to the CompositionText object. |
354 result = GetString(imm_context, lparam, GCS_COMPSTR, composition); | 376 ret = GetString(imm_context, lparam, GCS_COMPSTR, &composition->text); |
355 | 377 |
356 // This is a dirty workaround for facebook. Facebook deletes the placeholder | 378 if (ret) { |
357 // character (U+3000) used by Traditional-Chinese IMEs at the beginning of | 379 // This is a dirty workaround for facebook. Facebook deletes the |
358 // composition text. This prevents WebKit from replacing this placeholder | 380 // placeholder character (U+3000) used by Traditional-Chinese IMEs at the |
359 // character with a Traditional-Chinese character, i.e. we cannot input any | 381 // beginning of composition text. This prevents WebKit from replacing this |
360 // characters in a comment box of facebook with Traditional-Chinese IMEs. | 382 // placeholder character with a Traditional-Chinese character, i.e. we |
361 // As a workaround, we replace U+3000 at the beginning of composition text | 383 // cannot input any characters in a comment box of facebook with |
362 // with U+FF3F, a placeholder character used by Japanese IMEs. | 384 // Traditional-Chinese IMEs. As a workaround, we replace U+3000 at the |
363 if (input_language_id_ == MAKELANGID(LANG_CHINESE, | 385 // beginning of composition text with U+FF3F, a placeholder character used |
364 SUBLANG_CHINESE_TRADITIONAL) && | 386 // by Japanese IMEs. |
365 composition->ime_string[0] == 0x3000) { | 387 if (input_language_id_ == MAKELANGID(LANG_CHINESE, |
366 composition->ime_string[0] = 0xFF3F; | 388 SUBLANG_CHINESE_TRADITIONAL) && |
| 389 composition->text[0] == 0x3000) { |
| 390 composition->text[0] = 0xFF3F; |
| 391 } |
| 392 |
| 393 // Retrieve the composition underlines and selection range information. |
| 394 GetCompositionInfo(imm_context, lparam, composition); |
| 395 |
| 396 // Mark that there is an ongoing composition. |
| 397 is_composing_ = true; |
367 } | 398 } |
368 | 399 |
369 // Retrieve the composition underlines and selection range information. | |
370 GetCompositionInfo(imm_context, lparam, composition); | |
371 | |
372 // Mark that there is an ongoing composition. | |
373 is_composing_ = true; | |
374 | |
375 ::ImmReleaseContext(window_handle, imm_context); | 400 ::ImmReleaseContext(window_handle, imm_context); |
376 } | 401 } |
377 return result; | 402 return ret; |
378 } | 403 } |
379 | 404 |
380 void ImeInput::DisableIME(HWND window_handle) { | 405 void ImeInput::DisableIME(HWND window_handle) { |
381 // A renderer process have moved its input focus to a password input | 406 // A renderer process have moved its input focus to a password input |
382 // when there is an ongoing composition, e.g. a user has clicked a | 407 // when there is an ongoing composition, e.g. a user has clicked a |
383 // mouse button and selected a password input while composing a text. | 408 // mouse button and selected a password input while composing a text. |
384 // For this case, we have to complete the ongoing composition and | 409 // For this case, we have to complete the ongoing composition and |
385 // clean up the resources attached to this object BEFORE DISABLING THE IME. | 410 // clean up the resources attached to this object BEFORE DISABLING THE IME. |
386 CleanupComposition(window_handle); | 411 CleanupComposition(window_handle); |
387 ::ImmAssociateContextEx(window_handle, NULL, 0); | 412 ::ImmAssociateContextEx(window_handle, NULL, 0); |
(...skipping 26 matching lines...) Expand all Loading... |
414 if (caret_rect_ != caret_rect) { | 439 if (caret_rect_ != caret_rect) { |
415 caret_rect_ = caret_rect; | 440 caret_rect_ = caret_rect; |
416 // Move the IME windows. | 441 // Move the IME windows. |
417 HIMC imm_context = ::ImmGetContext(window_handle); | 442 HIMC imm_context = ::ImmGetContext(window_handle); |
418 if (imm_context) { | 443 if (imm_context) { |
419 MoveImeWindow(window_handle, imm_context); | 444 MoveImeWindow(window_handle, imm_context); |
420 ::ImmReleaseContext(window_handle, imm_context); | 445 ::ImmReleaseContext(window_handle, imm_context); |
421 } | 446 } |
422 } | 447 } |
423 } | 448 } |
| 449 |
| 450 std::string ImeInput::GetInputLanguageName() const { |
| 451 const LCID locale_id = MAKELCID(input_language_id_, SORT_DEFAULT); |
| 452 // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9. |
| 453 wchar_t buffer[9]; |
| 454 |
| 455 // Get language id. |
| 456 int length = ::GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &buffer[0], |
| 457 arraysize(buffer)); |
| 458 if (length <= 1) |
| 459 return std::string(); |
| 460 |
| 461 std::string language; |
| 462 WideToUTF8(buffer, length - 1, &language); |
| 463 if (SUBLANGID(input_language_id_) == SUBLANG_NEUTRAL) |
| 464 return language; |
| 465 |
| 466 // Get region id. |
| 467 length = ::GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &buffer[0], |
| 468 arraysize(buffer)); |
| 469 if (length <= 1) |
| 470 return language; |
| 471 |
| 472 std::string region; |
| 473 WideToUTF8(buffer, length - 1, ®ion); |
| 474 return language.append(1, '-').append(region); |
| 475 } |
| 476 |
| 477 base::i18n::TextDirection ImeInput::GetTextDirection() const { |
| 478 return IsRTLPrimaryLangID(PRIMARYLANGID(input_language_id_)) ? |
| 479 base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT; |
| 480 } |
| 481 |
| 482 // static |
| 483 bool ImeInput::IsRTLKeyboardLayoutInstalled() { |
| 484 static enum { |
| 485 RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED, |
| 486 RTL_KEYBOARD_LAYOUT_INSTALLED, |
| 487 RTL_KEYBOARD_LAYOUT_NOT_INSTALLED, |
| 488 RTL_KEYBOARD_LAYOUT_ERROR, |
| 489 } layout = RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED; |
| 490 |
| 491 // Cache the result value. |
| 492 if (layout != RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED) |
| 493 return layout == RTL_KEYBOARD_LAYOUT_INSTALLED; |
| 494 |
| 495 // Retrieve the number of layouts installed in this system. |
| 496 int size = GetKeyboardLayoutList(0, NULL); |
| 497 if (size <= 0) { |
| 498 layout = RTL_KEYBOARD_LAYOUT_ERROR; |
| 499 return false; |
| 500 } |
| 501 |
| 502 // Retrieve the keyboard layouts in an array and check if there is an RTL |
| 503 // layout in it. |
| 504 scoped_array<HKL> layouts(new HKL[size]); |
| 505 ::GetKeyboardLayoutList(size, layouts.get()); |
| 506 for (int i = 0; i < size; ++i) { |
| 507 if (IsRTLPrimaryLangID(PRIMARYLANGID(layouts[i]))) { |
| 508 layout = RTL_KEYBOARD_LAYOUT_INSTALLED; |
| 509 return true; |
| 510 } |
| 511 } |
| 512 |
| 513 layout = RTL_KEYBOARD_LAYOUT_NOT_INSTALLED; |
| 514 return false; |
| 515 } |
| 516 |
| 517 bool ImeInput::IsCtrlShiftPressed(base::i18n::TextDirection* direction) { |
| 518 uint8_t keystate[256]; |
| 519 if (!::GetKeyboardState(&keystate[0])) |
| 520 return false; |
| 521 |
| 522 // To check if a user is pressing only a control key and a right-shift key |
| 523 // (or a left-shift key), we use the steps below: |
| 524 // 1. Check if a user is pressing a control key and a right-shift key (or |
| 525 // a left-shift key). |
| 526 // 2. If the condition 1 is true, we should check if there are any other |
| 527 // keys pressed at the same time. |
| 528 // To ignore the keys checked in 1, we set their status to 0 before |
| 529 // checking the key status. |
| 530 const int kKeyDownMask = 0x80; |
| 531 if ((keystate[VK_CONTROL] & kKeyDownMask) == 0) |
| 532 return false; |
| 533 |
| 534 if (keystate[VK_RSHIFT] & kKeyDownMask) { |
| 535 keystate[VK_RSHIFT] = 0; |
| 536 *direction = base::i18n::RIGHT_TO_LEFT; |
| 537 } else if (keystate[VK_LSHIFT] & kKeyDownMask) { |
| 538 keystate[VK_LSHIFT] = 0; |
| 539 *direction = base::i18n::LEFT_TO_RIGHT; |
| 540 } else { |
| 541 return false; |
| 542 } |
| 543 |
| 544 // Scan the key status to find pressed keys. We should adandon changing the |
| 545 // text direction when there are other pressed keys. |
| 546 // This code is executed only when a user is pressing a control key and a |
| 547 // right-shift key (or a left-shift key), i.e. we should ignore the status of |
| 548 // the keys: VK_SHIFT, VK_CONTROL, VK_RCONTROL, and VK_LCONTROL. |
| 549 // So, we reset their status to 0 and ignore them. |
| 550 keystate[VK_SHIFT] = 0; |
| 551 keystate[VK_CONTROL] = 0; |
| 552 keystate[VK_RCONTROL] = 0; |
| 553 keystate[VK_LCONTROL] = 0; |
| 554 for (int i = 0; i <= VK_PACKET; ++i) { |
| 555 if (keystate[i] & kKeyDownMask) |
| 556 return false; |
| 557 } |
| 558 return true; |
| 559 } |
| 560 |
| 561 } // namespace ui |
OLD | NEW |