OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "views/ime/input_method_win.h" |
| 6 |
| 7 #include "base/basictypes.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" |
| 10 #include "ui/base/keycodes/keyboard_codes.h" |
| 11 #include "views/events/event.h" |
| 12 |
| 13 namespace views { |
| 14 |
| 15 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate) |
| 16 : active_(false), |
| 17 direction_(base::i18n::UNKNOWN_DIRECTION), |
| 18 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) { |
| 19 set_delegate(delegate); |
| 20 } |
| 21 |
| 22 InputMethodWin::~InputMethodWin() { |
| 23 if (widget()) |
| 24 ime_input_.DisableIME(hwnd()); |
| 25 } |
| 26 |
| 27 void InputMethodWin::Init(Widget* widget) { |
| 28 InputMethodBase::Init(widget); |
| 29 |
| 30 // Gets the initial input locale and text direction information. |
| 31 OnInputLangChange(0, 0); |
| 32 } |
| 33 |
| 34 void InputMethodWin::DispatchKeyEvent(const KeyEvent& key) { |
| 35 // Handles ctrl-shift key to change text direction and layout alignment. |
| 36 if (ui::ImeInput::IsRTLKeyboardLayoutInstalled() && !IsTextInputTypeNone()) { |
| 37 ui::KeyboardCode code = key.key_code(); |
| 38 if (key.type() == ui::ET_KEY_PRESSED) { |
| 39 if (code == ui::VKEY_SHIFT) { |
| 40 base::i18n::TextDirection dir; |
| 41 if (ui::ImeInput::IsCtrlShiftPressed(&dir)) |
| 42 pending_requested_direction_ = dir; |
| 43 } else if (code != ui::VKEY_CONTROL) { |
| 44 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; |
| 45 } |
| 46 } else if (key.type() == ui::ET_KEY_RELEASED && |
| 47 (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) && |
| 48 pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) { |
| 49 GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment( |
| 50 pending_requested_direction_); |
| 51 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; |
| 52 } |
| 53 } |
| 54 |
| 55 DispatchKeyEventPostIME(key); |
| 56 } |
| 57 |
| 58 void InputMethodWin::OnTextInputTypeChanged(View* view) { |
| 59 if (IsViewFocused(view)) { |
| 60 ime_input_.CancelIME(hwnd()); |
| 61 UpdateIMEState(); |
| 62 } |
| 63 } |
| 64 |
| 65 void InputMethodWin::OnCaretBoundsChanged(View* view) { |
| 66 gfx::Rect rect; |
| 67 if (!IsViewFocused(view) || !GetCaretBoundsInWidget(&rect)) |
| 68 return; |
| 69 ime_input_.UpdateCaretRect(hwnd(), rect); |
| 70 } |
| 71 |
| 72 void InputMethodWin::CancelComposition(View* view) { |
| 73 if (IsViewFocused(view)) |
| 74 ime_input_.CancelIME(hwnd()); |
| 75 } |
| 76 |
| 77 std::string InputMethodWin::GetInputLocale() { |
| 78 return locale_; |
| 79 } |
| 80 |
| 81 base::i18n::TextDirection InputMethodWin::GetInputTextDirection() { |
| 82 return direction_; |
| 83 } |
| 84 |
| 85 bool InputMethodWin::IsActive() { |
| 86 return active_; |
| 87 } |
| 88 |
| 89 void InputMethodWin::FocusedViewWillChange() { |
| 90 ConfirmCompositionText(); |
| 91 } |
| 92 |
| 93 void InputMethodWin::FocusedViewDidChange() { |
| 94 UpdateIMEState(); |
| 95 } |
| 96 |
| 97 void InputMethodWin::OnSetFocus() { |
| 98 if (!widget_focused()) { |
| 99 set_widget_focused(true); |
| 100 UpdateIMEState(); |
| 101 } |
| 102 } |
| 103 |
| 104 void InputMethodWin::OnKillFocus() { |
| 105 if (widget_focused()) { |
| 106 ConfirmCompositionText(); |
| 107 set_widget_focused(false); |
| 108 } |
| 109 } |
| 110 |
| 111 void InputMethodWin::OnInputLangChange(DWORD character_set, |
| 112 HKL input_language_id) { |
| 113 active_ = ime_input_.SetInputLanguage(); |
| 114 locale_ = ime_input_.GetInputLanguageName(); |
| 115 direction_ = ime_input_.GetTextDirection(); |
| 116 OnInputMethodChanged(); |
| 117 } |
| 118 |
| 119 LRESULT InputMethodWin::OnImeSetContext( |
| 120 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 121 active_ = (wparam == TRUE); |
| 122 if (active_) |
| 123 ime_input_.CreateImeWindow(hwnd()); |
| 124 |
| 125 OnInputMethodChanged(); |
| 126 return ime_input_.SetImeWindowStyle(hwnd(), message, wparam, lparam, handled); |
| 127 } |
| 128 |
| 129 LRESULT InputMethodWin::OnImeStartComposition( |
| 130 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 131 // We have to prevent WTL from calling ::DefWindowProc() because the function |
| 132 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to |
| 133 // over-write the position of IME windows. |
| 134 *handled = TRUE; |
| 135 |
| 136 if (IsTextInputTypeNone()) |
| 137 return 0; |
| 138 |
| 139 // Reset the composition status and create IME windows. |
| 140 ime_input_.CreateImeWindow(hwnd()); |
| 141 ime_input_.ResetComposition(hwnd()); |
| 142 return 0; |
| 143 } |
| 144 |
| 145 LRESULT InputMethodWin::OnImeComposition( |
| 146 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 147 // We have to prevent WTL from calling ::DefWindowProc() because we do not |
| 148 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. |
| 149 *handled = TRUE; |
| 150 |
| 151 if (IsTextInputTypeNone()) |
| 152 return 0; |
| 153 |
| 154 // At first, update the position of the IME window. |
| 155 ime_input_.UpdateImeWindow(hwnd()); |
| 156 |
| 157 // Retrieve the result string and its attributes of the ongoing composition |
| 158 // and send it to a renderer process. |
| 159 ui::CompositionText composition; |
| 160 if (ime_input_.GetResult(hwnd(), lparam, &composition.text)) { |
| 161 GetTextInputClient()->InsertText(composition.text); |
| 162 ime_input_.ResetComposition(hwnd()); |
| 163 // Fall though and try reading the composition string. |
| 164 // Japanese IMEs send a message containing both GCS_RESULTSTR and |
| 165 // GCS_COMPSTR, which means an ongoing composition has been finished |
| 166 // by the start of another composition. |
| 167 } |
| 168 // Retrieve the composition string and its attributes of the ongoing |
| 169 // composition and send it to a renderer process. |
| 170 if (ime_input_.GetComposition(hwnd(), lparam, &composition)) |
| 171 GetTextInputClient()->SetCompositionText(composition); |
| 172 |
| 173 return 0; |
| 174 } |
| 175 |
| 176 LRESULT InputMethodWin::OnImeEndComposition( |
| 177 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 178 // Let WTL call ::DefWindowProc() and release its resources. |
| 179 *handled = FALSE; |
| 180 |
| 181 if (IsTextInputTypeNone()) |
| 182 return 0; |
| 183 |
| 184 if (GetTextInputClient()->HasCompositionText()) |
| 185 GetTextInputClient()->ClearCompositionText(); |
| 186 |
| 187 ime_input_.ResetComposition(hwnd()); |
| 188 ime_input_.DestroyImeWindow(hwnd()); |
| 189 return 0; |
| 190 } |
| 191 |
| 192 LRESULT InputMethodWin::OnChar( |
| 193 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 194 *handled = TRUE; |
| 195 |
| 196 // We need to send character events to the focused text input client event if |
| 197 // its text input type is ui::TEXT_INPUT_TYPE_NONE. |
| 198 if (!GetTextInputClient()) |
| 199 return 0; |
| 200 |
| 201 int flags = 0; |
| 202 flags |= (::GetKeyState(VK_MENU) & 0x80)? ui::EF_ALT_DOWN : 0; |
| 203 flags |= (::GetKeyState(VK_SHIFT) & 0x80)? ui::EF_SHIFT_DOWN : 0; |
| 204 flags |= (::GetKeyState(VK_CONTROL) & 0x80)? ui::EF_CONTROL_DOWN : 0; |
| 205 GetTextInputClient()->InsertChar(static_cast<char16>(wparam), flags); |
| 206 return 0; |
| 207 } |
| 208 |
| 209 LRESULT InputMethodWin::OnDeadChar( |
| 210 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| 211 *handled = TRUE; |
| 212 |
| 213 if (IsTextInputTypeNone()) |
| 214 return 0; |
| 215 |
| 216 // Shows the dead character as a composition text, so that the user can know |
| 217 // what dead key was pressed. |
| 218 ui::CompositionText composition; |
| 219 composition.text.assign(1, static_cast<char16>(wparam)); |
| 220 composition.selection = ui::Range(0, 1); |
| 221 composition.underlines.push_back( |
| 222 ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); |
| 223 GetTextInputClient()->SetCompositionText(composition); |
| 224 return 0; |
| 225 } |
| 226 |
| 227 void InputMethodWin::ConfirmCompositionText() { |
| 228 if (!IsTextInputTypeNone()) { |
| 229 ime_input_.CleanupComposition(hwnd()); |
| 230 // Though above line should confirm the client's composition text by sending |
| 231 // a result text to us, in case the input method and the client are in |
| 232 // inconsistent states, we check the client's composition state again. |
| 233 if (GetTextInputClient()->HasCompositionText()) |
| 234 GetTextInputClient()->ConfirmCompositionText(); |
| 235 } |
| 236 } |
| 237 |
| 238 void InputMethodWin::UpdateIMEState() { |
| 239 // Use switch here in case we are going to add more text input types. |
| 240 // We disable input method in password field. |
| 241 switch (GetTextInputType()) { |
| 242 case ui::TEXT_INPUT_TYPE_NONE: |
| 243 case ui::TEXT_INPUT_TYPE_PASSWORD: |
| 244 ime_input_.DisableIME(hwnd()); |
| 245 break; |
| 246 default: |
| 247 ime_input_.EnableIME(hwnd()); |
| 248 break; |
| 249 } |
| 250 } |
| 251 |
| 252 } // namespace views |
OLD | NEW |