Chromium Code Reviews| 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 : delegate_(delegate), | |
| 17 widget_(NULL), | |
| 18 focused_view_(NULL), | |
| 19 widget_focused_(false), | |
| 20 active_(false), | |
| 21 direction_(base::i18n::UNKNOWN_DIRECTION), | |
| 22 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) { | |
| 23 } | |
| 24 | |
| 25 InputMethodWin::~InputMethodWin() { | |
| 26 if (widget_) { | |
| 27 ime_input_.DisableIME(hwnd()); | |
| 28 widget_->GetFocusManager()->RemoveFocusChangeListener(this); | |
| 29 widget_ = NULL; | |
| 30 } | |
| 31 } | |
| 32 | |
| 33 void InputMethodWin::set_delegate(internal::InputMethodDelegate* delegate) { | |
| 34 delegate_ = delegate; | |
| 35 } | |
| 36 | |
| 37 void InputMethodWin::Init(Widget* widget) { | |
| 38 DCHECK(widget); | |
| 39 DCHECK(widget->GetFocusManager()); | |
| 40 DCHECK(widget->GetNativeView()); | |
| 41 | |
| 42 if (widget_ == widget) { | |
| 43 DLOG(ERROR) << "Already initialized."; | |
| 44 return; | |
| 45 } | |
| 46 | |
| 47 DCHECK(!widget_); | |
| 48 | |
| 49 widget_ = widget; | |
| 50 widget->GetFocusManager()->AddFocusChangeListener(this); | |
| 51 | |
| 52 // Gets the initial input locale and text direction information. | |
| 53 OnInputLangChange(0, 0); | |
| 54 } | |
| 55 | |
| 56 void InputMethodWin::DispatchKeyEvent(const KeyEvent& key) { | |
| 57 // Handles ctrl-shift key to change text direction and layout alignment. | |
| 58 if (ui::ImeInput::IsRTLKeyboardLayoutInstalled() && | |
| 59 GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { | |
| 60 ui::KeyboardCode code = key.key_code(); | |
| 61 if (key.type() == ui::ET_KEY_PRESSED) { | |
| 62 if (code == ui::VKEY_SHIFT) { | |
| 63 base::i18n::TextDirection dir; | |
| 64 if (ui::ImeInput::IsCtrlShiftPressed(&dir)) | |
| 65 pending_requested_direction_ = dir; | |
| 66 } else if (code != ui::VKEY_CONTROL) { | |
| 67 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; | |
| 68 } | |
| 69 } else if (key.type() == ui::ET_KEY_RELEASED && | |
| 70 (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) && | |
| 71 pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) { | |
| 72 GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment( | |
| 73 pending_requested_direction_); | |
| 74 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 DispatchKeyEventPostIME(key); | |
| 79 } | |
| 80 | |
| 81 void InputMethodWin::OnTextInputTypeChanged(View* view) { | |
| 82 if (IsViewFocused(view)) { | |
| 83 ime_input_.CancelIME(hwnd()); | |
| 84 UpdateIMEState(); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 void InputMethodWin::OnCaretBoundsChanged(View* view) { | |
| 89 if (!IsViewFocused(view)) | |
| 90 return; | |
| 91 | |
| 92 TextInputClient* client = view->GetTextInputClient(); | |
| 93 if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 94 return; | |
| 95 | |
| 96 gfx::Rect rect = client->GetCaretBounds(); | |
| 97 gfx::Point origin = rect.origin(); | |
| 98 gfx::Point end = gfx::Point(rect.right(), rect.bottom()); | |
| 99 | |
| 100 View::ConvertPointToWidget(view, &origin); | |
| 101 View::ConvertPointToWidget(view, &end); | |
| 102 | |
| 103 // We need to translate the coordinates to the toplevel widget if the view is | |
| 104 // inside a child widget. | |
| 105 if (view->GetWidget() != widget_) { | |
| 106 RECT win_rect; | |
| 107 win_rect.left = origin.x(); | |
| 108 win_rect.top = origin.y(); | |
| 109 win_rect.right = end.x(); | |
| 110 win_rect.bottom = end.y(); | |
| 111 | |
| 112 ::MapWindowPoints(view->GetWidget()->GetNativeView(), hwnd(), | |
| 113 reinterpret_cast<LPPOINT>(&win_rect), | |
| 114 sizeof(RECT)/sizeof(POINT)); | |
| 115 rect = win_rect; | |
|
oshima
2011/03/22 03:35:00
looks like this method is very similar to one in g
James Su
2011/03/22 08:39:35
Done.
| |
| 116 } else { | |
| 117 rect = gfx::Rect(origin, gfx::Size(end.x() - origin.x(), | |
| 118 end.y() - origin.y())); | |
| 119 } | |
| 120 | |
| 121 ime_input_.UpdateCaretRect(hwnd(), rect); | |
|
oshima
2011/03/22 03:35:00
and define template method to implement this for e
James Su
2011/03/22 08:39:35
Done.
| |
| 122 } | |
| 123 | |
| 124 void InputMethodWin::CancelComposition(View* view) { | |
| 125 if (IsViewFocused(view)) | |
| 126 ime_input_.CancelIME(hwnd()); | |
| 127 } | |
| 128 | |
| 129 std::string InputMethodWin::GetInputLocale() { | |
| 130 return locale_; | |
| 131 } | |
| 132 | |
| 133 base::i18n::TextDirection InputMethodWin::GetInputTextDirection() { | |
| 134 return direction_; | |
| 135 } | |
| 136 | |
| 137 bool InputMethodWin::IsActive() { | |
| 138 return active_; | |
| 139 } | |
| 140 | |
| 141 void InputMethodWin::FocusWillChange(View* focused_before, View* focused) { | |
| 142 DCHECK_EQ(focused_view_, focused_before); | |
| 143 ConfirmComposition(); | |
| 144 focused_view_ = focused; | |
| 145 UpdateIMEState(); | |
| 146 } | |
| 147 | |
| 148 void InputMethodWin::OnSetFocus() { | |
| 149 if (!widget_focused_) { | |
| 150 widget_focused_ = true; | |
| 151 UpdateIMEState(); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 void InputMethodWin::OnKillFocus() { | |
| 156 if (widget_focused_) { | |
| 157 ConfirmComposition(); | |
| 158 widget_focused_ = false; | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 void InputMethodWin::OnInputLangChange(DWORD character_set, | |
| 163 HKL input_language_id) { | |
| 164 active_ = ime_input_.SetInputLanguage(); | |
| 165 locale_ = ime_input_.GetInputLanguageName(); | |
| 166 direction_ = ime_input_.GetTextDirection(); | |
| 167 OnInputMethodChanged(); | |
| 168 } | |
| 169 | |
| 170 LRESULT InputMethodWin::OnImeSetContext( | |
| 171 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 172 active_ = (wparam == TRUE); | |
| 173 if (active_) | |
| 174 ime_input_.CreateImeWindow(hwnd()); | |
| 175 | |
| 176 OnInputMethodChanged(); | |
| 177 return ime_input_.SetImeWindowStyle(hwnd(), message, wparam, lparam, handled); | |
| 178 } | |
| 179 | |
| 180 LRESULT InputMethodWin::OnImeStartComposition( | |
| 181 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 182 // We have to prevent WTL from calling ::DefWindowProc() because the function | |
| 183 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to | |
| 184 // over-write the position of IME windows. | |
| 185 *handled = TRUE; | |
| 186 | |
| 187 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 188 return 0; | |
| 189 | |
| 190 // Reset the composition status and create IME windows. | |
| 191 ime_input_.CreateImeWindow(hwnd()); | |
| 192 ime_input_.ResetComposition(hwnd()); | |
| 193 return 0; | |
| 194 } | |
| 195 | |
| 196 LRESULT InputMethodWin::OnImeComposition( | |
| 197 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 198 // We have to prevent WTL from calling ::DefWindowProc() because we do not | |
| 199 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. | |
| 200 *handled = TRUE; | |
| 201 | |
| 202 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 203 return 0; | |
| 204 | |
| 205 // At first, update the position of the IME window. | |
| 206 ime_input_.UpdateImeWindow(hwnd()); | |
| 207 | |
| 208 // Retrieve the result string and its attributes of the ongoing composition | |
| 209 // and send it to a renderer process. | |
| 210 ui::Composition composition; | |
| 211 if (ime_input_.GetResult(hwnd(), lparam, &composition.text)) { | |
| 212 GetTextInputClient()->InsertText(composition.text); | |
| 213 ime_input_.ResetComposition(hwnd()); | |
| 214 // Fall though and try reading the composition string. | |
| 215 // Japanese IMEs send a message containing both GCS_RESULTSTR and | |
| 216 // GCS_COMPSTR, which means an ongoing composition has been finished | |
| 217 // by the start of another composition. | |
| 218 } | |
| 219 // Retrieve the composition string and its attributes of the ongoing | |
| 220 // composition and send it to a renderer process. | |
| 221 if (ime_input_.GetComposition(hwnd(), lparam, &composition)) | |
| 222 GetTextInputClient()->SetComposition(composition); | |
| 223 | |
| 224 return 0; | |
| 225 } | |
| 226 | |
| 227 LRESULT InputMethodWin::OnImeEndComposition( | |
| 228 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 229 // Let WTL call ::DefWindowProc() and release its resources. | |
| 230 *handled = FALSE; | |
| 231 | |
| 232 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 233 return 0; | |
| 234 | |
| 235 if (GetTextInputClient()->HasComposition()) | |
| 236 GetTextInputClient()->ClearComposition(); | |
| 237 | |
| 238 ime_input_.ResetComposition(hwnd()); | |
| 239 ime_input_.DestroyImeWindow(hwnd()); | |
| 240 return 0; | |
| 241 } | |
| 242 | |
| 243 LRESULT InputMethodWin::OnChar( | |
| 244 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 245 *handled = TRUE; | |
| 246 | |
| 247 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 248 return 0; | |
| 249 | |
| 250 int flags = 0; | |
| 251 flags |= (::GetKeyState(VK_MENU) & 0x80)? ui::EF_ALT_DOWN : 0; | |
| 252 flags |= (::GetKeyState(VK_SHIFT) & 0x80)? ui::EF_SHIFT_DOWN : 0; | |
| 253 flags |= (::GetKeyState(VK_CONTROL) & 0x80)? ui::EF_CONTROL_DOWN : 0; | |
| 254 GetTextInputClient()->InsertChar(static_cast<char16>(wparam), flags); | |
| 255 return 0; | |
| 256 } | |
| 257 | |
| 258 LRESULT InputMethodWin::OnDeadChar( | |
| 259 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { | |
| 260 *handled = TRUE; | |
| 261 | |
| 262 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
| 263 return 0; | |
| 264 | |
| 265 // Shows the dead character as a composition text, so that the user can know | |
| 266 // what dead key was pressed. | |
| 267 ui::Composition composition; | |
| 268 composition.text.assign(1, static_cast<char16>(wparam)); | |
| 269 composition.selection = ui::Range(0, 1); | |
| 270 composition.underlines.push_back( | |
| 271 ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); | |
| 272 GetTextInputClient()->SetComposition(composition); | |
| 273 return 0; | |
| 274 } | |
| 275 | |
| 276 void InputMethodWin::ConfirmComposition() { | |
| 277 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { | |
| 278 ime_input_.CleanupComposition(hwnd()); | |
| 279 if (GetTextInputClient()->HasComposition()) | |
| 280 GetTextInputClient()->ConfirmComposition(); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void InputMethodWin::UpdateIMEState() { | |
| 285 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_TEXT) | |
| 286 ime_input_.EnableIME(hwnd()); | |
| 287 else | |
| 288 ime_input_.DisableIME(hwnd()); | |
| 289 } | |
| 290 | |
| 291 TextInputClient* InputMethodWin::GetTextInputClient() const { | |
| 292 return (widget_focused_ && focused_view_) ? | |
| 293 focused_view_->GetTextInputClient() : NULL; | |
| 294 } | |
| 295 | |
| 296 ui::TextInputType InputMethodWin::GetTextInputType() const { | |
| 297 TextInputClient* client = GetTextInputClient(); | |
| 298 return client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE; | |
| 299 } | |
| 300 | |
| 301 bool InputMethodWin::IsViewFocused(View* view) const { | |
| 302 return widget_focused_ && view && focused_view_ == view; | |
| 303 } | |
| 304 | |
| 305 void InputMethodWin::DispatchKeyEventPostIME(const KeyEvent& key) const { | |
| 306 if (delegate_) | |
| 307 delegate_->DispatchKeyEventPostIME(key); | |
| 308 } | |
| 309 | |
| 310 void InputMethodWin::OnInputMethodChanged() const { | |
| 311 TextInputClient* client = GetTextInputClient(); | |
| 312 if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) | |
| 313 client->OnInputMethodChanged(); | |
| 314 } | |
| 315 | |
| 316 } // namespace views | |
| OLD | NEW |