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