| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/base/ime/input_method_win.h" | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "ui/base/ime/text_input_client.h" | |
| 9 #include "ui/base/ime/win/tsf_input_scope.h" | |
| 10 #include "ui/events/event.h" | |
| 11 #include "ui/events/event_constants.h" | |
| 12 #include "ui/events/event_utils.h" | |
| 13 #include "ui/events/keycodes/keyboard_codes.h" | |
| 14 #include "ui/gfx/win/dpi.h" | |
| 15 #include "ui/gfx/win/hwnd_util.h" | |
| 16 | |
| 17 namespace ui { | |
| 18 namespace { | |
| 19 | |
| 20 // Extra number of chars before and after selection (or composition) range which | |
| 21 // is returned to IME for improving conversion accuracy. | |
| 22 static const size_t kExtraNumberOfChars = 20; | |
| 23 | |
| 24 } // namespace | |
| 25 | |
| 26 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate, | |
| 27 HWND toplevel_window_handle) | |
| 28 : toplevel_window_handle_(toplevel_window_handle), | |
| 29 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION), | |
| 30 accept_carriage_return_(false), | |
| 31 active_(false), | |
| 32 enabled_(false), | |
| 33 is_candidate_popup_open_(false), | |
| 34 composing_window_handle_(NULL) { | |
| 35 SetDelegate(delegate); | |
| 36 // In non-Aura environment, appropriate callbacks to OnFocus() and OnBlur() | |
| 37 // are not implemented yet. To work around this limitation, here we use | |
| 38 // "always focused" model. | |
| 39 // TODO(ime): Fix the caller of OnFocus() and OnBlur() so that appropriate | |
| 40 // focus event will be passed. | |
| 41 InputMethodBase::OnFocus(); | |
| 42 } | |
| 43 | |
| 44 void InputMethodWin::Init(bool focused) { | |
| 45 // Gets the initial input locale. | |
| 46 OnInputLocaleChanged(); | |
| 47 | |
| 48 InputMethodBase::Init(focused); | |
| 49 } | |
| 50 | |
| 51 void InputMethodWin::OnFocus() { | |
| 52 // Ignore OnFocus event for "always focused" model. See the comment in the | |
| 53 // constructor. | |
| 54 // TODO(ime): Implement OnFocus once the callers are fixed. | |
| 55 } | |
| 56 | |
| 57 void InputMethodWin::OnBlur() { | |
| 58 // Ignore OnBlur event for "always focused" model. See the comment in the | |
| 59 // constructor. | |
| 60 // TODO(ime): Implement OnFocus once the callers are fixed. | |
| 61 } | |
| 62 | |
| 63 bool InputMethodWin::OnUntranslatedIMEMessage( | |
| 64 const base::NativeEvent& event, | |
| 65 InputMethod::NativeEventResult* result) { | |
| 66 LRESULT original_result = 0; | |
| 67 BOOL handled = FALSE; | |
| 68 switch (event.message) { | |
| 69 case WM_IME_SETCONTEXT: | |
| 70 original_result = OnImeSetContext( | |
| 71 event.hwnd, event.message, event.wParam, event.lParam, &handled); | |
| 72 break; | |
| 73 case WM_IME_STARTCOMPOSITION: | |
| 74 original_result = OnImeStartComposition( | |
| 75 event.hwnd, event.message, event.wParam, event.lParam, &handled); | |
| 76 break; | |
| 77 case WM_IME_COMPOSITION: | |
| 78 original_result = OnImeComposition( | |
| 79 event.hwnd, event.message, event.wParam, event.lParam, &handled); | |
| 80 break; | |
| 81 case WM_IME_ENDCOMPOSITION: | |
| 82 original_result = OnImeEndComposition( | |
| 83 event.hwnd, event.message, event.wParam, event.lParam, &handled); | |
| 84 break; | |
| 85 case WM_IME_REQUEST: | |
| 86 original_result = OnImeRequest( | |
| 87 event.message, event.wParam, event.lParam, &handled); | |
| 88 break; | |
| 89 case WM_CHAR: | |
| 90 case WM_SYSCHAR: | |
| 91 original_result = OnChar( | |
| 92 event.hwnd, event.message, event.wParam, event.lParam, &handled); | |
| 93 break; | |
| 94 case WM_IME_NOTIFY: | |
| 95 original_result = OnImeNotify( | |
| 96 event.message, event.wParam, event.lParam, &handled); | |
| 97 break; | |
| 98 default: | |
| 99 NOTREACHED() << "Unknown IME message:" << event.message; | |
| 100 break; | |
| 101 } | |
| 102 if (result) | |
| 103 *result = original_result; | |
| 104 return !!handled; | |
| 105 } | |
| 106 | |
| 107 bool InputMethodWin::DispatchKeyEvent(const ui::KeyEvent& event) { | |
| 108 if (!event.HasNativeEvent()) | |
| 109 return DispatchFabricatedKeyEvent(event); | |
| 110 | |
| 111 const base::NativeEvent& native_key_event = event.native_event(); | |
| 112 if (native_key_event.message == WM_CHAR) { | |
| 113 BOOL handled; | |
| 114 OnChar(native_key_event.hwnd, native_key_event.message, | |
| 115 native_key_event.wParam, native_key_event.lParam, &handled); | |
| 116 return !!handled; // Don't send WM_CHAR for post event processing. | |
| 117 } | |
| 118 // Handles ctrl-shift key to change text direction and layout alignment. | |
| 119 if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled() && | |
| 120 !IsTextInputTypeNone()) { | |
| 121 // TODO: shouldn't need to generate a KeyEvent here. | |
| 122 const ui::KeyEvent key(native_key_event); | |
| 123 ui::KeyboardCode code = key.key_code(); | |
| 124 if (key.type() == ui::ET_KEY_PRESSED) { | |
| 125 if (code == ui::VKEY_SHIFT) { | |
| 126 base::i18n::TextDirection dir; | |
| 127 if (ui::IMM32Manager::IsCtrlShiftPressed(&dir)) | |
| 128 pending_requested_direction_ = dir; | |
| 129 } else if (code != ui::VKEY_CONTROL) { | |
| 130 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; | |
| 131 } | |
| 132 } else if (key.type() == ui::ET_KEY_RELEASED && | |
| 133 (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) && | |
| 134 pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) { | |
| 135 GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment( | |
| 136 pending_requested_direction_); | |
| 137 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 return DispatchKeyEventPostIME(event); | |
| 142 } | |
| 143 | |
| 144 void InputMethodWin::OnTextInputTypeChanged(const TextInputClient* client) { | |
| 145 if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) | |
| 146 return; | |
| 147 imm32_manager_.CancelIME(GetAttachedWindowHandle(client)); | |
| 148 UpdateIMEState(); | |
| 149 } | |
| 150 | |
| 151 void InputMethodWin::OnCaretBoundsChanged(const TextInputClient* client) { | |
| 152 if (!enabled_ || !IsTextInputClientFocused(client) || | |
| 153 !IsWindowFocused(client)) { | |
| 154 return; | |
| 155 } | |
| 156 // The current text input type should not be NONE if |client| is focused. | |
| 157 DCHECK(!IsTextInputTypeNone()); | |
| 158 // Tentatively assume that the returned value is DIP (Density Independent | |
| 159 // Pixel). See the comment in text_input_client.h and http://crbug.com/360334. | |
| 160 const gfx::Rect dip_screen_bounds(GetTextInputClient()->GetCaretBounds()); | |
| 161 const gfx::Rect screen_bounds = gfx::win::DIPToScreenRect(dip_screen_bounds); | |
| 162 | |
| 163 HWND attached_window = GetAttachedWindowHandle(client); | |
| 164 // TODO(ime): see comment in TextInputClient::GetCaretBounds(), this | |
| 165 // conversion shouldn't be necessary. | |
| 166 RECT r = {}; | |
| 167 GetClientRect(attached_window, &r); | |
| 168 POINT window_point = { screen_bounds.x(), screen_bounds.y() }; | |
| 169 ScreenToClient(attached_window, &window_point); | |
| 170 gfx::Rect caret_rect(gfx::Point(window_point.x, window_point.y), | |
| 171 screen_bounds.size()); | |
| 172 imm32_manager_.UpdateCaretRect(attached_window, caret_rect); | |
| 173 } | |
| 174 | |
| 175 void InputMethodWin::CancelComposition(const TextInputClient* client) { | |
| 176 if (enabled_ && IsTextInputClientFocused(client)) | |
| 177 imm32_manager_.CancelIME(GetAttachedWindowHandle(client)); | |
| 178 } | |
| 179 | |
| 180 void InputMethodWin::OnInputLocaleChanged() { | |
| 181 active_ = imm32_manager_.SetInputLanguage(); | |
| 182 locale_ = imm32_manager_.GetInputLanguageName(); | |
| 183 OnInputMethodChanged(); | |
| 184 } | |
| 185 | |
| 186 std::string InputMethodWin::GetInputLocale() { | |
| 187 return locale_; | |
| 188 } | |
| 189 | |
| 190 bool InputMethodWin::IsActive() { | |
| 191 return active_; | |
| 192 } | |
| 193 | |
| 194 bool InputMethodWin::IsCandidatePopupOpen() const { | |
| 195 return is_candidate_popup_open_; | |
| 196 } | |
| 197 | |
| 198 void InputMethodWin::OnWillChangeFocusedClient(TextInputClient* focused_before, | |
| 199 TextInputClient* focused) { | |
| 200 if (IsWindowFocused(focused_before)) | |
| 201 ConfirmCompositionText(); | |
| 202 } | |
| 203 | |
| 204 void InputMethodWin::OnDidChangeFocusedClient( | |
| 205 TextInputClient* focused_before, | |
| 206 TextInputClient* focused) { | |
| 207 if (IsWindowFocused(focused)) { | |
| 208 // Force to update the input type since client's TextInputStateChanged() | |
| 209 // function might not be called if text input types before the client loses | |
| 210 // focus and after it acquires focus again are the same. | |
| 211 OnTextInputTypeChanged(focused); | |
| 212 | |
| 213 UpdateIMEState(); | |
| 214 | |
| 215 // Force to update caret bounds, in case the client thinks that the caret | |
| 216 // bounds has not changed. | |
| 217 OnCaretBoundsChanged(focused); | |
| 218 } | |
| 219 if (focused_before != focused) | |
| 220 accept_carriage_return_ = false; | |
| 221 } | |
| 222 | |
| 223 LRESULT InputMethodWin::OnChar(HWND window_handle, | |
| 224 UINT message, | |
| 225 WPARAM wparam, | |
| 226 LPARAM lparam, | |
| 227 BOOL* handled) { | |
| 228 *handled = TRUE; | |
| 229 | |
| 230 // We need to send character events to the focused text input client event if | |
| 231 // its text input type is ui::TEXT_INPUT_TYPE_NONE. | |
| 232 if (GetTextInputClient()) { | |
| 233 const base::char16 kCarriageReturn = L'\r'; | |
| 234 const base::char16 ch = static_cast<base::char16>(wparam); | |
| 235 // A mask to determine the previous key state from |lparam|. The value is 1 | |
| 236 // if the key is down before the message is sent, or it is 0 if the key is | |
| 237 // up. | |
| 238 const uint32 kPrevKeyDownBit = 0x40000000; | |
| 239 if (ch == kCarriageReturn && !(lparam & kPrevKeyDownBit)) | |
| 240 accept_carriage_return_ = true; | |
| 241 // Conditionally ignore '\r' events to work around crbug.com/319100. | |
| 242 // TODO(yukawa, IME): Figure out long-term solution. | |
| 243 if (ch != kCarriageReturn || accept_carriage_return_) | |
| 244 GetTextInputClient()->InsertChar(ch, ui::GetModifiersFromKeyState()); | |
| 245 } | |
| 246 | |
| 247 // Explicitly show the system menu at a good location on [Alt]+[Space]. | |
| 248 // Note: Setting |handled| to FALSE for DefWindowProc triggering of the system | |
| 249 // menu causes undesirable titlebar artifacts in the classic theme. | |
| 250 if (message == WM_SYSCHAR && wparam == VK_SPACE) | |
| 251 gfx::ShowSystemMenu(window_handle); | |
| 252 | |
| 253 return 0; | |
| 254 } | |
| 255 | |
| 256 LRESULT InputMethodWin::OnImeSetContext(HWND window_handle, | |
| 257 UINT message, | |
| 258 WPARAM wparam, | |
| 259 LPARAM lparam, | |
| 260 BOOL* handled) { | |
| 261 if (!!wparam) | |
| 262 imm32_manager_.CreateImeWindow(window_handle); | |
| 263 | |
| 264 OnInputMethodChanged(); | |
| 265 return imm32_manager_.SetImeWindowStyle( | |
| 266 window_handle, message, wparam, lparam, handled); | |
| 267 } | |
| 268 | |
| 269 LRESULT InputMethodWin::OnImeStartComposition(HWND window_handle, | |
| 270 UINT message, | |
| 271 WPARAM wparam, | |
| 272 LPARAM lparam, | |
| 273 BOOL* handled) { | |
| 274 // We have to prevent WTL from calling ::DefWindowProc() because the function | |
| 275 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to | |
| 276 // over-write the position of IME windows. | |
| 277 *handled = TRUE; | |
| 278 | |
| 279 // Reset the composition status and create IME windows. | |
| 280 composing_window_handle_ = window_handle; | |
| 281 imm32_manager_.CreateImeWindow(window_handle); | |
| 282 imm32_manager_.ResetComposition(window_handle); | |
| 283 return 0; | |
| 284 } | |
| 285 | |
| 286 LRESULT InputMethodWin::OnImeComposition(HWND window_handle, | |
| 287 UINT message, | |
| 288 WPARAM wparam, | |
| 289 LPARAM lparam, | |
| 290 BOOL* handled) { | |
| 291 // We have to prevent WTL from calling ::DefWindowProc() because we do not | |
| 292 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. | |
| 293 *handled = TRUE; | |
| 294 | |
| 295 // At first, update the position of the IME window. | |
| 296 imm32_manager_.UpdateImeWindow(window_handle); | |
| 297 | |
| 298 // Retrieve the result string and its attributes of the ongoing composition | |
| 299 // and send it to a renderer process. | |
| 300 ui::CompositionText composition; | |
| 301 if (imm32_manager_.GetResult(window_handle, lparam, &composition.text)) { | |
| 302 if (!IsTextInputTypeNone()) | |
| 303 GetTextInputClient()->InsertText(composition.text); | |
| 304 imm32_manager_.ResetComposition(window_handle); | |
| 305 // Fall though and try reading the composition string. | |
| 306 // Japanese IMEs send a message containing both GCS_RESULTSTR and | |
| 307 // GCS_COMPSTR, which means an ongoing composition has been finished | |
| 308 // by the start of another composition. | |
| 309 } | |
| 310 // Retrieve the composition string and its attributes of the ongoing | |
| 311 // composition and send it to a renderer process. | |
| 312 if (imm32_manager_.GetComposition(window_handle, lparam, &composition) && | |
| 313 !IsTextInputTypeNone()) | |
| 314 GetTextInputClient()->SetCompositionText(composition); | |
| 315 | |
| 316 return 0; | |
| 317 } | |
| 318 | |
| 319 LRESULT InputMethodWin::OnImeEndComposition(HWND window_handle, | |
| 320 UINT message, | |
| 321 WPARAM wparam, | |
| 322 LPARAM lparam, | |
| 323 BOOL* handled) { | |
| 324 // Let WTL call ::DefWindowProc() and release its resources. | |
| 325 *handled = FALSE; | |
| 326 | |
| 327 composing_window_handle_ = NULL; | |
| 328 | |
| 329 if (!IsTextInputTypeNone() && GetTextInputClient()->HasCompositionText()) | |
| 330 GetTextInputClient()->ClearCompositionText(); | |
| 331 | |
| 332 imm32_manager_.ResetComposition(window_handle); | |
| 333 imm32_manager_.DestroyImeWindow(window_handle); | |
| 334 return 0; | |
| 335 } | |
| 336 | |
| 337 LRESULT InputMethodWin::OnImeNotify(UINT message, | |
| 338 WPARAM wparam, | |
| 339 LPARAM lparam, | |
| 340 BOOL* handled) { | |
| 341 *handled = FALSE; | |
| 342 | |
| 343 bool previous_state = is_candidate_popup_open_; | |
| 344 | |
| 345 // Update |is_candidate_popup_open_|, whether a candidate window is open. | |
| 346 switch (wparam) { | |
| 347 case IMN_OPENCANDIDATE: | |
| 348 is_candidate_popup_open_ = true; | |
| 349 if (!previous_state) | |
| 350 OnCandidateWindowShown(); | |
| 351 break; | |
| 352 case IMN_CLOSECANDIDATE: | |
| 353 is_candidate_popup_open_ = false; | |
| 354 if (previous_state) | |
| 355 OnCandidateWindowHidden(); | |
| 356 break; | |
| 357 case IMN_CHANGECANDIDATE: | |
| 358 // TODO(kochi): The IME API expects this event to notify window size change, | |
| 359 // while this may fire more often without window resize. There is no generic | |
| 360 // way to get bounds of candidate window. | |
| 361 OnCandidateWindowUpdated(); | |
| 362 break; | |
| 363 } | |
| 364 | |
| 365 return 0; | |
| 366 } | |
| 367 | |
| 368 LRESULT InputMethodWin::OnImeRequest(UINT message, | |
| 369 WPARAM wparam, | |
| 370 LPARAM lparam, | |
| 371 BOOL* handled) { | |
| 372 *handled = FALSE; | |
| 373 | |
| 374 // Should not receive WM_IME_REQUEST message, if IME is disabled. | |
| 375 const ui::TextInputType type = GetTextInputType(); | |
| 376 if (type == ui::TEXT_INPUT_TYPE_NONE || | |
| 377 type == ui::TEXT_INPUT_TYPE_PASSWORD) { | |
| 378 return 0; | |
| 379 } | |
| 380 | |
| 381 switch (wparam) { | |
| 382 case IMR_RECONVERTSTRING: | |
| 383 *handled = TRUE; | |
| 384 return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); | |
| 385 case IMR_DOCUMENTFEED: | |
| 386 *handled = TRUE; | |
| 387 return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); | |
| 388 case IMR_QUERYCHARPOSITION: | |
| 389 *handled = TRUE; | |
| 390 return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam)); | |
| 391 default: | |
| 392 return 0; | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) { | |
| 397 ui::TextInputClient* client = GetTextInputClient(); | |
| 398 if (!client) | |
| 399 return 0; | |
| 400 | |
| 401 gfx::Range text_range; | |
| 402 if (!client->GetTextRange(&text_range) || text_range.is_empty()) | |
| 403 return 0; | |
| 404 | |
| 405 bool result = false; | |
| 406 gfx::Range target_range; | |
| 407 if (client->HasCompositionText()) | |
| 408 result = client->GetCompositionTextRange(&target_range); | |
| 409 | |
| 410 if (!result || target_range.is_empty()) { | |
| 411 if (!client->GetSelectionRange(&target_range) || | |
| 412 !target_range.IsValid()) { | |
| 413 return 0; | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 if (!text_range.Contains(target_range)) | |
| 418 return 0; | |
| 419 | |
| 420 if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars) | |
| 421 text_range.set_start(target_range.GetMin() - kExtraNumberOfChars); | |
| 422 | |
| 423 if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars) | |
| 424 text_range.set_end(target_range.GetMax() + kExtraNumberOfChars); | |
| 425 | |
| 426 size_t len = text_range.length(); | |
| 427 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); | |
| 428 | |
| 429 if (!reconv) | |
| 430 return need_size; | |
| 431 | |
| 432 if (reconv->dwSize < need_size) | |
| 433 return 0; | |
| 434 | |
| 435 base::string16 text; | |
| 436 if (!GetTextInputClient()->GetTextFromRange(text_range, &text)) | |
| 437 return 0; | |
| 438 DCHECK_EQ(text_range.length(), text.length()); | |
| 439 | |
| 440 reconv->dwVersion = 0; | |
| 441 reconv->dwStrLen = len; | |
| 442 reconv->dwStrOffset = sizeof(RECONVERTSTRING); | |
| 443 reconv->dwCompStrLen = | |
| 444 client->HasCompositionText() ? target_range.length() : 0; | |
| 445 reconv->dwCompStrOffset = | |
| 446 (target_range.GetMin() - text_range.start()) * sizeof(WCHAR); | |
| 447 reconv->dwTargetStrLen = target_range.length(); | |
| 448 reconv->dwTargetStrOffset = reconv->dwCompStrOffset; | |
| 449 | |
| 450 memcpy((char*)reconv + sizeof(RECONVERTSTRING), | |
| 451 text.c_str(), len * sizeof(WCHAR)); | |
| 452 | |
| 453 // According to Microsoft API document, IMR_RECONVERTSTRING and | |
| 454 // IMR_DOCUMENTFEED should return reconv, but some applications return | |
| 455 // need_size. | |
| 456 return reinterpret_cast<LRESULT>(reconv); | |
| 457 } | |
| 458 | |
| 459 LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) { | |
| 460 ui::TextInputClient* client = GetTextInputClient(); | |
| 461 if (!client) | |
| 462 return 0; | |
| 463 | |
| 464 // If there is a composition string already, we don't allow reconversion. | |
| 465 if (client->HasCompositionText()) | |
| 466 return 0; | |
| 467 | |
| 468 gfx::Range text_range; | |
| 469 if (!client->GetTextRange(&text_range) || text_range.is_empty()) | |
| 470 return 0; | |
| 471 | |
| 472 gfx::Range selection_range; | |
| 473 if (!client->GetSelectionRange(&selection_range) || | |
| 474 selection_range.is_empty()) { | |
| 475 return 0; | |
| 476 } | |
| 477 | |
| 478 DCHECK(text_range.Contains(selection_range)); | |
| 479 | |
| 480 size_t len = selection_range.length(); | |
| 481 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); | |
| 482 | |
| 483 if (!reconv) | |
| 484 return need_size; | |
| 485 | |
| 486 if (reconv->dwSize < need_size) | |
| 487 return 0; | |
| 488 | |
| 489 // TODO(penghuang): Return some extra context to help improve IME's | |
| 490 // reconversion accuracy. | |
| 491 base::string16 text; | |
| 492 if (!GetTextInputClient()->GetTextFromRange(selection_range, &text)) | |
| 493 return 0; | |
| 494 DCHECK_EQ(selection_range.length(), text.length()); | |
| 495 | |
| 496 reconv->dwVersion = 0; | |
| 497 reconv->dwStrLen = len; | |
| 498 reconv->dwStrOffset = sizeof(RECONVERTSTRING); | |
| 499 reconv->dwCompStrLen = len; | |
| 500 reconv->dwCompStrOffset = 0; | |
| 501 reconv->dwTargetStrLen = len; | |
| 502 reconv->dwTargetStrOffset = 0; | |
| 503 | |
| 504 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), | |
| 505 text.c_str(), len * sizeof(WCHAR)); | |
| 506 | |
| 507 // According to Microsoft API document, IMR_RECONVERTSTRING and | |
| 508 // IMR_DOCUMENTFEED should return reconv, but some applications return | |
| 509 // need_size. | |
| 510 return reinterpret_cast<LRESULT>(reconv); | |
| 511 } | |
| 512 | |
| 513 LRESULT InputMethodWin::OnQueryCharPosition(IMECHARPOSITION* char_positon) { | |
| 514 if (!char_positon) | |
| 515 return 0; | |
| 516 | |
| 517 if (char_positon->dwSize < sizeof(IMECHARPOSITION)) | |
| 518 return 0; | |
| 519 | |
| 520 ui::TextInputClient* client = GetTextInputClient(); | |
| 521 if (!client) | |
| 522 return 0; | |
| 523 | |
| 524 // Tentatively assume that the returned value from |client| is DIP (Density | |
| 525 // Independent Pixel). See the comment in text_input_client.h and | |
| 526 // http://crbug.com/360334. | |
| 527 gfx::Rect dip_rect; | |
| 528 if (client->HasCompositionText()) { | |
| 529 if (!client->GetCompositionCharacterBounds(char_positon->dwCharPos, | |
| 530 &dip_rect)) { | |
| 531 return 0; | |
| 532 } | |
| 533 } else { | |
| 534 // If there is no composition and the first character is queried, returns | |
| 535 // the caret bounds. This behavior is the same to that of RichEdit control. | |
| 536 if (char_positon->dwCharPos != 0) | |
| 537 return 0; | |
| 538 dip_rect = client->GetCaretBounds(); | |
| 539 } | |
| 540 const gfx::Rect rect = gfx::win::DIPToScreenRect(dip_rect); | |
| 541 | |
| 542 char_positon->pt.x = rect.x(); | |
| 543 char_positon->pt.y = rect.y(); | |
| 544 char_positon->cLineHeight = rect.height(); | |
| 545 return 1; // returns non-zero value when succeeded. | |
| 546 } | |
| 547 | |
| 548 HWND InputMethodWin::GetAttachedWindowHandle( | |
| 549 const TextInputClient* text_input_client) const { | |
| 550 // On Aura environment, we can assume that |toplevel_window_handle_| always | |
| 551 // represents the valid top-level window handle because each top-level window | |
| 552 // is responsible for lifecycle management of corresponding InputMethod | |
| 553 // instance. | |
| 554 return toplevel_window_handle_; | |
| 555 } | |
| 556 | |
| 557 bool InputMethodWin::IsWindowFocused(const TextInputClient* client) const { | |
| 558 if (!client) | |
| 559 return false; | |
| 560 HWND attached_window_handle = GetAttachedWindowHandle(client); | |
| 561 // When Aura is enabled, |attached_window_handle| should always be a top-level | |
| 562 // window. So we can safely assume that |attached_window_handle| is ready for | |
| 563 // receiving keyboard input as long as it is an active window. This works well | |
| 564 // even when the |attached_window_handle| becomes active but has not received | |
| 565 // WM_FOCUS yet. | |
| 566 return attached_window_handle && GetActiveWindow() == attached_window_handle; | |
| 567 } | |
| 568 | |
| 569 bool InputMethodWin::DispatchFabricatedKeyEvent(const ui::KeyEvent& event) { | |
| 570 if (event.is_char()) { | |
| 571 if (GetTextInputClient()) { | |
| 572 GetTextInputClient()->InsertChar( | |
| 573 static_cast<base::char16>(event.key_code()), | |
| 574 ui::GetModifiersFromKeyState()); | |
| 575 return true; | |
| 576 } | |
| 577 } | |
| 578 return DispatchKeyEventPostIME(event); | |
| 579 } | |
| 580 | |
| 581 void InputMethodWin::ConfirmCompositionText() { | |
| 582 if (composing_window_handle_) | |
| 583 imm32_manager_.CleanupComposition(composing_window_handle_); | |
| 584 | |
| 585 if (!IsTextInputTypeNone()) { | |
| 586 // Though above line should confirm the client's composition text by sending | |
| 587 // a result text to us, in case the input method and the client are in | |
| 588 // inconsistent states, we check the client's composition state again. | |
| 589 if (GetTextInputClient()->HasCompositionText()) | |
| 590 GetTextInputClient()->ConfirmCompositionText(); | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 void InputMethodWin::UpdateIMEState() { | |
| 595 // Use switch here in case we are going to add more text input types. | |
| 596 // We disable input method in password field. | |
| 597 const HWND window_handle = GetAttachedWindowHandle(GetTextInputClient()); | |
| 598 const TextInputType text_input_type = GetTextInputType(); | |
| 599 const TextInputMode text_input_mode = GetTextInputMode(); | |
| 600 switch (text_input_type) { | |
| 601 case ui::TEXT_INPUT_TYPE_NONE: | |
| 602 case ui::TEXT_INPUT_TYPE_PASSWORD: | |
| 603 imm32_manager_.DisableIME(window_handle); | |
| 604 enabled_ = false; | |
| 605 break; | |
| 606 default: | |
| 607 imm32_manager_.EnableIME(window_handle); | |
| 608 enabled_ = true; | |
| 609 break; | |
| 610 } | |
| 611 | |
| 612 imm32_manager_.SetTextInputMode(window_handle, text_input_mode); | |
| 613 tsf_inputscope::SetInputScopeForTsfUnawareWindow( | |
| 614 window_handle, text_input_type, text_input_mode); | |
| 615 } | |
| 616 | |
| 617 } // namespace ui | |
| OLD | NEW |