OLD | NEW |
1 // Copyright (c) 2011 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 "views/ime/input_method_win.h" | 5 #include "views/ime/input_method_win.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "ui/base/keycodes/keyboard_codes.h" | 10 #include "ui/base/keycodes/keyboard_codes.h" |
11 #include "views/events/event.h" | 11 #include "views/events/event.h" |
12 | 12 |
| 13 // Extra number of chars before and after selection (or composition) range which |
| 14 // is returned to IME for improving conversion accuracy. |
| 15 static const size_t kExtraNumberOfChars = 20; |
| 16 |
13 namespace views { | 17 namespace views { |
14 | 18 |
15 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate) | 19 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate) |
16 : active_(false), | 20 : active_(false), |
17 direction_(base::i18n::UNKNOWN_DIRECTION), | 21 direction_(base::i18n::UNKNOWN_DIRECTION), |
18 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) { | 22 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) { |
19 set_delegate(delegate); | 23 set_delegate(delegate); |
20 } | 24 } |
21 | 25 |
22 InputMethodWin::~InputMethodWin() { | 26 InputMethodWin::~InputMethodWin() { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 break; | 112 break; |
109 case WM_IME_STARTCOMPOSITION: | 113 case WM_IME_STARTCOMPOSITION: |
110 result = OnImeStartComposition(message, w_param, l_param, handled); | 114 result = OnImeStartComposition(message, w_param, l_param, handled); |
111 break; | 115 break; |
112 case WM_IME_COMPOSITION: | 116 case WM_IME_COMPOSITION: |
113 result = OnImeComposition(message, w_param, l_param, handled); | 117 result = OnImeComposition(message, w_param, l_param, handled); |
114 break; | 118 break; |
115 case WM_IME_ENDCOMPOSITION: | 119 case WM_IME_ENDCOMPOSITION: |
116 result = OnImeEndComposition(message, w_param, l_param, handled); | 120 result = OnImeEndComposition(message, w_param, l_param, handled); |
117 break; | 121 break; |
| 122 case WM_IME_REQUEST: |
| 123 result = OnImeRequest(message, w_param, l_param, handled); |
| 124 break; |
118 case WM_CHAR: | 125 case WM_CHAR: |
119 case WM_SYSCHAR: | 126 case WM_SYSCHAR: |
120 result = OnChar(message, w_param, l_param, handled); | 127 result = OnChar(message, w_param, l_param, handled); |
121 break; | 128 break; |
122 case WM_DEADCHAR: | 129 case WM_DEADCHAR: |
123 case WM_SYSDEADCHAR: | 130 case WM_SYSDEADCHAR: |
124 result = OnDeadChar(message, w_param, l_param, handled); | 131 result = OnDeadChar(message, w_param, l_param, handled); |
125 break; | 132 break; |
126 default: | 133 default: |
127 NOTREACHED() << "Unknown IME message:" << message; | 134 NOTREACHED() << "Unknown IME message:" << message; |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 return 0; | 219 return 0; |
213 | 220 |
214 if (GetTextInputClient()->HasCompositionText()) | 221 if (GetTextInputClient()->HasCompositionText()) |
215 GetTextInputClient()->ClearCompositionText(); | 222 GetTextInputClient()->ClearCompositionText(); |
216 | 223 |
217 ime_input_.ResetComposition(hwnd()); | 224 ime_input_.ResetComposition(hwnd()); |
218 ime_input_.DestroyImeWindow(hwnd()); | 225 ime_input_.DestroyImeWindow(hwnd()); |
219 return 0; | 226 return 0; |
220 } | 227 } |
221 | 228 |
| 229 LRESULT InputMethodWin::OnImeRequest( |
| 230 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 231 *handled = FALSE; |
| 232 |
| 233 // Should not receive WM_IME_REQUEST message, if IME is disabled. |
| 234 const ui::TextInputType type = GetTextInputType(); |
| 235 if (type == ui::TEXT_INPUT_TYPE_NONE || |
| 236 type == ui::TEXT_INPUT_TYPE_PASSWORD) { |
| 237 return 0; |
| 238 } |
| 239 |
| 240 switch (wparam) { |
| 241 case IMR_RECONVERTSTRING: |
| 242 *handled = TRUE; |
| 243 return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); |
| 244 case IMR_DOCUMENTFEED: |
| 245 *handled = TRUE; |
| 246 return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); |
| 247 default: |
| 248 return 0; |
| 249 } |
| 250 } |
| 251 |
222 LRESULT InputMethodWin::OnChar( | 252 LRESULT InputMethodWin::OnChar( |
223 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | 253 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
224 *handled = TRUE; | 254 *handled = TRUE; |
225 | 255 |
226 // We need to send character events to the focused text input client event if | 256 // We need to send character events to the focused text input client event if |
227 // its text input type is ui::TEXT_INPUT_TYPE_NONE. | 257 // its text input type is ui::TEXT_INPUT_TYPE_NONE. |
228 if (!GetTextInputClient()) | 258 if (!GetTextInputClient()) |
229 return 0; | 259 return 0; |
230 | 260 |
231 int flags = 0; | 261 int flags = 0; |
(...skipping 15 matching lines...) Expand all Loading... |
247 // what dead key was pressed. | 277 // what dead key was pressed. |
248 ui::CompositionText composition; | 278 ui::CompositionText composition; |
249 composition.text.assign(1, static_cast<char16>(wparam)); | 279 composition.text.assign(1, static_cast<char16>(wparam)); |
250 composition.selection = ui::Range(0, 1); | 280 composition.selection = ui::Range(0, 1); |
251 composition.underlines.push_back( | 281 composition.underlines.push_back( |
252 ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); | 282 ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); |
253 GetTextInputClient()->SetCompositionText(composition); | 283 GetTextInputClient()->SetCompositionText(composition); |
254 return 0; | 284 return 0; |
255 } | 285 } |
256 | 286 |
| 287 LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) { |
| 288 TextInputClient* client = GetTextInputClient(); |
| 289 if (!client) |
| 290 return 0; |
| 291 |
| 292 ui::Range text_range; |
| 293 if (!client->GetTextRange(&text_range) || text_range.is_empty()) |
| 294 return 0; |
| 295 |
| 296 bool result = false; |
| 297 ui::Range target_range; |
| 298 if (client->HasCompositionText()) |
| 299 result = client->GetCompositionTextRange(&target_range); |
| 300 |
| 301 if (!result || target_range.is_empty()) { |
| 302 if (!client->GetSelectionRange(&target_range) || |
| 303 !target_range.IsValid()) { |
| 304 return 0; |
| 305 } |
| 306 } |
| 307 |
| 308 if (!text_range.Contains(target_range)) |
| 309 return 0; |
| 310 |
| 311 if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars) |
| 312 text_range.set_start(target_range.GetMin() - kExtraNumberOfChars); |
| 313 |
| 314 if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars) |
| 315 text_range.set_end(target_range.GetMax() + kExtraNumberOfChars); |
| 316 |
| 317 size_t len = text_range.length(); |
| 318 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); |
| 319 |
| 320 if (!reconv) |
| 321 return need_size; |
| 322 |
| 323 if (reconv->dwSize < need_size) |
| 324 return 0; |
| 325 |
| 326 string16 text; |
| 327 if (!GetTextInputClient()->GetTextFromRange(text_range, &text)) |
| 328 return 0; |
| 329 DCHECK_EQ(text_range.length(), text.length()); |
| 330 |
| 331 reconv->dwVersion = 0; |
| 332 reconv->dwStrLen = len; |
| 333 reconv->dwStrOffset = sizeof(RECONVERTSTRING); |
| 334 reconv->dwCompStrLen = |
| 335 client->HasCompositionText() ? target_range.length() : 0; |
| 336 reconv->dwCompStrOffset = |
| 337 (target_range.GetMin() - text_range.start()) * sizeof(WCHAR); |
| 338 reconv->dwTargetStrLen = target_range.length(); |
| 339 reconv->dwTargetStrOffset = reconv->dwCompStrOffset; |
| 340 |
| 341 memcpy((char*)reconv + sizeof(RECONVERTSTRING), |
| 342 text.c_str(), len * sizeof(WCHAR)); |
| 343 |
| 344 // According to Microsft API document, IMR_RECONVERTSTRING and |
| 345 // IMR_DOCUMENTFEED should return reconv, but some applications return |
| 346 // need_size. |
| 347 return reinterpret_cast<LRESULT>(reconv); |
| 348 } |
| 349 |
| 350 LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) { |
| 351 TextInputClient* client = GetTextInputClient(); |
| 352 if (!client) |
| 353 return 0; |
| 354 |
| 355 // If there is a composition string already, we don't allow reconversion. |
| 356 if (client->HasCompositionText()) |
| 357 return 0; |
| 358 |
| 359 ui::Range text_range; |
| 360 if (!client->GetTextRange(&text_range) || text_range.is_empty()) |
| 361 return 0; |
| 362 |
| 363 ui::Range selection_range; |
| 364 if (!client->GetSelectionRange(&selection_range) || |
| 365 selection_range.is_empty()) { |
| 366 return 0; |
| 367 } |
| 368 |
| 369 DCHECK(text_range.Contains(selection_range)); |
| 370 |
| 371 size_t len = selection_range.length(); |
| 372 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); |
| 373 |
| 374 if (!reconv) |
| 375 return need_size; |
| 376 |
| 377 if (reconv->dwSize < need_size) |
| 378 return 0; |
| 379 |
| 380 // TODO(penghuang): Return some extra context to help improve IME's |
| 381 // reconversion accuracy. |
| 382 string16 text; |
| 383 if (!GetTextInputClient()->GetTextFromRange(selection_range, &text)) |
| 384 return 0; |
| 385 DCHECK_EQ(selection_range.length(), text.length()); |
| 386 |
| 387 reconv->dwVersion = 0; |
| 388 reconv->dwStrLen = len; |
| 389 reconv->dwStrOffset = sizeof(RECONVERTSTRING); |
| 390 reconv->dwCompStrLen = len; |
| 391 reconv->dwCompStrOffset = 0; |
| 392 reconv->dwTargetStrLen = len; |
| 393 reconv->dwTargetStrOffset = 0; |
| 394 |
| 395 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), |
| 396 text.c_str(), len * sizeof(WCHAR)); |
| 397 |
| 398 // According to Microsft API document, IMR_RECONVERTSTRING and |
| 399 // IMR_DOCUMENTFEED should return reconv, but some applications return |
| 400 // need_size. |
| 401 return reinterpret_cast<LRESULT>(reconv); |
| 402 } |
| 403 |
257 void InputMethodWin::ConfirmCompositionText() { | 404 void InputMethodWin::ConfirmCompositionText() { |
258 if (!IsTextInputTypeNone()) { | 405 if (!IsTextInputTypeNone()) { |
259 ime_input_.CleanupComposition(hwnd()); | 406 ime_input_.CleanupComposition(hwnd()); |
260 // Though above line should confirm the client's composition text by sending | 407 // Though above line should confirm the client's composition text by sending |
261 // a result text to us, in case the input method and the client are in | 408 // a result text to us, in case the input method and the client are in |
262 // inconsistent states, we check the client's composition state again. | 409 // inconsistent states, we check the client's composition state again. |
263 if (GetTextInputClient()->HasCompositionText()) | 410 if (GetTextInputClient()->HasCompositionText()) |
264 GetTextInputClient()->ConfirmCompositionText(); | 411 GetTextInputClient()->ConfirmCompositionText(); |
265 } | 412 } |
266 } | 413 } |
267 | 414 |
268 void InputMethodWin::UpdateIMEState() { | 415 void InputMethodWin::UpdateIMEState() { |
269 // Use switch here in case we are going to add more text input types. | 416 // Use switch here in case we are going to add more text input types. |
270 // We disable input method in password field. | 417 // We disable input method in password field. |
271 switch (GetTextInputType()) { | 418 switch (GetTextInputType()) { |
272 case ui::TEXT_INPUT_TYPE_NONE: | 419 case ui::TEXT_INPUT_TYPE_NONE: |
273 case ui::TEXT_INPUT_TYPE_PASSWORD: | 420 case ui::TEXT_INPUT_TYPE_PASSWORD: |
274 ime_input_.DisableIME(hwnd()); | 421 ime_input_.DisableIME(hwnd()); |
275 break; | 422 break; |
276 default: | 423 default: |
277 ime_input_.EnableIME(hwnd()); | 424 ime_input_.EnableIME(hwnd()); |
278 break; | 425 break; |
279 } | 426 } |
280 } | 427 } |
281 | 428 |
282 } // namespace views | 429 } // namespace views |
OLD | NEW |