Chromium Code Reviews| Index: views/ime/input_method_win.cc |
| diff --git a/views/ime/input_method_win.cc b/views/ime/input_method_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..007a0a65da69334832a7fe736bbeac7d5a215fd1 |
| --- /dev/null |
| +++ b/views/ime/input_method_win.cc |
| @@ -0,0 +1,316 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "views/ime/input_method_win.h" |
| + |
| +#include "base/basictypes.h" |
| +#include "base/logging.h" |
| +#include "base/string_util.h" |
| +#include "ui/base/keycodes/keyboard_codes.h" |
| +#include "views/events/event.h" |
| + |
| +namespace views { |
| + |
| +InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate) |
| + : delegate_(delegate), |
| + widget_(NULL), |
| + focused_view_(NULL), |
| + widget_focused_(false), |
| + active_(false), |
| + direction_(base::i18n::UNKNOWN_DIRECTION), |
| + pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) { |
| +} |
| + |
| +InputMethodWin::~InputMethodWin() { |
| + if (widget_) { |
| + ime_input_.DisableIME(hwnd()); |
| + widget_->GetFocusManager()->RemoveFocusChangeListener(this); |
| + widget_ = NULL; |
| + } |
| +} |
| + |
| +void InputMethodWin::set_delegate(internal::InputMethodDelegate* delegate) { |
| + delegate_ = delegate; |
| +} |
| + |
| +void InputMethodWin::Init(Widget* widget) { |
| + DCHECK(widget); |
| + DCHECK(widget->GetFocusManager()); |
| + DCHECK(widget->GetNativeView()); |
| + |
| + if (widget_ == widget) { |
| + DLOG(ERROR) << "Already initialized."; |
| + return; |
| + } |
| + |
| + DCHECK(!widget_); |
| + |
| + widget_ = widget; |
| + widget->GetFocusManager()->AddFocusChangeListener(this); |
| + |
| + // Gets the initial input locale and text direction information. |
| + OnInputLangChange(0, 0); |
| +} |
| + |
| +void InputMethodWin::DispatchKeyEvent(const KeyEvent& key) { |
| + // Handles ctrl-shift key to change text direction and layout alignment. |
| + if (ui::ImeInput::IsRTLKeyboardLayoutInstalled() && |
| + GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { |
| + ui::KeyboardCode code = key.key_code(); |
| + if (key.type() == ui::ET_KEY_PRESSED) { |
| + if (code == ui::VKEY_SHIFT) { |
| + base::i18n::TextDirection dir; |
| + if (ui::ImeInput::IsCtrlShiftPressed(&dir)) |
| + pending_requested_direction_ = dir; |
| + } else if (code != ui::VKEY_CONTROL) { |
| + pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; |
| + } |
| + } else if (key.type() == ui::ET_KEY_RELEASED && |
| + (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) && |
| + pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) { |
| + GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment( |
| + pending_requested_direction_); |
| + pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION; |
| + } |
| + } |
| + |
| + DispatchKeyEventPostIME(key); |
| +} |
| + |
| +void InputMethodWin::OnTextInputTypeChanged(View* view) { |
| + if (IsViewFocused(view)) { |
| + ime_input_.CancelIME(hwnd()); |
| + UpdateIMEState(); |
| + } |
| +} |
| + |
| +void InputMethodWin::OnCaretBoundsChanged(View* view) { |
| + if (!IsViewFocused(view)) |
| + return; |
| + |
| + TextInputClient* client = view->GetTextInputClient(); |
| + if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return; |
| + |
| + gfx::Rect rect = client->GetCaretBounds(); |
| + gfx::Point origin = rect.origin(); |
| + gfx::Point end = gfx::Point(rect.right(), rect.bottom()); |
| + |
| + View::ConvertPointToWidget(view, &origin); |
| + View::ConvertPointToWidget(view, &end); |
| + |
| + // We need to translate the coordinates to the toplevel widget if the view is |
| + // inside a child widget. |
| + if (view->GetWidget() != widget_) { |
| + RECT win_rect; |
| + win_rect.left = origin.x(); |
| + win_rect.top = origin.y(); |
| + win_rect.right = end.x(); |
| + win_rect.bottom = end.y(); |
| + |
| + ::MapWindowPoints(view->GetWidget()->GetNativeView(), hwnd(), |
| + reinterpret_cast<LPPOINT>(&win_rect), |
| + sizeof(RECT)/sizeof(POINT)); |
| + 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.
|
| + } else { |
| + rect = gfx::Rect(origin, gfx::Size(end.x() - origin.x(), |
| + end.y() - origin.y())); |
| + } |
| + |
| + 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.
|
| +} |
| + |
| +void InputMethodWin::CancelComposition(View* view) { |
| + if (IsViewFocused(view)) |
| + ime_input_.CancelIME(hwnd()); |
| +} |
| + |
| +std::string InputMethodWin::GetInputLocale() { |
| + return locale_; |
| +} |
| + |
| +base::i18n::TextDirection InputMethodWin::GetInputTextDirection() { |
| + return direction_; |
| +} |
| + |
| +bool InputMethodWin::IsActive() { |
| + return active_; |
| +} |
| + |
| +void InputMethodWin::FocusWillChange(View* focused_before, View* focused) { |
| + DCHECK_EQ(focused_view_, focused_before); |
| + ConfirmComposition(); |
| + focused_view_ = focused; |
| + UpdateIMEState(); |
| +} |
| + |
| +void InputMethodWin::OnSetFocus() { |
| + if (!widget_focused_) { |
| + widget_focused_ = true; |
| + UpdateIMEState(); |
| + } |
| +} |
| + |
| +void InputMethodWin::OnKillFocus() { |
| + if (widget_focused_) { |
| + ConfirmComposition(); |
| + widget_focused_ = false; |
| + } |
| +} |
| + |
| +void InputMethodWin::OnInputLangChange(DWORD character_set, |
| + HKL input_language_id) { |
| + active_ = ime_input_.SetInputLanguage(); |
| + locale_ = ime_input_.GetInputLanguageName(); |
| + direction_ = ime_input_.GetTextDirection(); |
| + OnInputMethodChanged(); |
| +} |
| + |
| +LRESULT InputMethodWin::OnImeSetContext( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + active_ = (wparam == TRUE); |
| + if (active_) |
| + ime_input_.CreateImeWindow(hwnd()); |
| + |
| + OnInputMethodChanged(); |
| + return ime_input_.SetImeWindowStyle(hwnd(), message, wparam, lparam, handled); |
| +} |
| + |
| +LRESULT InputMethodWin::OnImeStartComposition( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + // We have to prevent WTL from calling ::DefWindowProc() because the function |
| + // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to |
| + // over-write the position of IME windows. |
| + *handled = TRUE; |
| + |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return 0; |
| + |
| + // Reset the composition status and create IME windows. |
| + ime_input_.CreateImeWindow(hwnd()); |
| + ime_input_.ResetComposition(hwnd()); |
| + return 0; |
| +} |
| + |
| +LRESULT InputMethodWin::OnImeComposition( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + // We have to prevent WTL from calling ::DefWindowProc() because we do not |
| + // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. |
| + *handled = TRUE; |
| + |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return 0; |
| + |
| + // At first, update the position of the IME window. |
| + ime_input_.UpdateImeWindow(hwnd()); |
| + |
| + // Retrieve the result string and its attributes of the ongoing composition |
| + // and send it to a renderer process. |
| + ui::Composition composition; |
| + if (ime_input_.GetResult(hwnd(), lparam, &composition.text)) { |
| + GetTextInputClient()->InsertText(composition.text); |
| + ime_input_.ResetComposition(hwnd()); |
| + // Fall though and try reading the composition string. |
| + // Japanese IMEs send a message containing both GCS_RESULTSTR and |
| + // GCS_COMPSTR, which means an ongoing composition has been finished |
| + // by the start of another composition. |
| + } |
| + // Retrieve the composition string and its attributes of the ongoing |
| + // composition and send it to a renderer process. |
| + if (ime_input_.GetComposition(hwnd(), lparam, &composition)) |
| + GetTextInputClient()->SetComposition(composition); |
| + |
| + return 0; |
| +} |
| + |
| +LRESULT InputMethodWin::OnImeEndComposition( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + // Let WTL call ::DefWindowProc() and release its resources. |
| + *handled = FALSE; |
| + |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return 0; |
| + |
| + if (GetTextInputClient()->HasComposition()) |
| + GetTextInputClient()->ClearComposition(); |
| + |
| + ime_input_.ResetComposition(hwnd()); |
| + ime_input_.DestroyImeWindow(hwnd()); |
| + return 0; |
| +} |
| + |
| +LRESULT InputMethodWin::OnChar( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + *handled = TRUE; |
| + |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return 0; |
| + |
| + int flags = 0; |
| + flags |= (::GetKeyState(VK_MENU) & 0x80)? ui::EF_ALT_DOWN : 0; |
| + flags |= (::GetKeyState(VK_SHIFT) & 0x80)? ui::EF_SHIFT_DOWN : 0; |
| + flags |= (::GetKeyState(VK_CONTROL) & 0x80)? ui::EF_CONTROL_DOWN : 0; |
| + GetTextInputClient()->InsertChar(static_cast<char16>(wparam), flags); |
| + return 0; |
| +} |
| + |
| +LRESULT InputMethodWin::OnDeadChar( |
| + UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) { |
| + *handled = TRUE; |
| + |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| + return 0; |
| + |
| + // Shows the dead character as a composition text, so that the user can know |
| + // what dead key was pressed. |
| + ui::Composition composition; |
| + composition.text.assign(1, static_cast<char16>(wparam)); |
| + composition.selection = ui::Range(0, 1); |
| + composition.underlines.push_back( |
| + ui::CompositionUnderline(0, 1, SK_ColorBLACK, false)); |
| + GetTextInputClient()->SetComposition(composition); |
| + return 0; |
| +} |
| + |
| +void InputMethodWin::ConfirmComposition() { |
| + if (GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { |
| + ime_input_.CleanupComposition(hwnd()); |
| + if (GetTextInputClient()->HasComposition()) |
| + GetTextInputClient()->ConfirmComposition(); |
| + } |
| +} |
| + |
| +void InputMethodWin::UpdateIMEState() { |
| + if (GetTextInputType() == ui::TEXT_INPUT_TYPE_TEXT) |
| + ime_input_.EnableIME(hwnd()); |
| + else |
| + ime_input_.DisableIME(hwnd()); |
| +} |
| + |
| +TextInputClient* InputMethodWin::GetTextInputClient() const { |
| + return (widget_focused_ && focused_view_) ? |
| + focused_view_->GetTextInputClient() : NULL; |
| +} |
| + |
| +ui::TextInputType InputMethodWin::GetTextInputType() const { |
| + TextInputClient* client = GetTextInputClient(); |
| + return client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE; |
| +} |
| + |
| +bool InputMethodWin::IsViewFocused(View* view) const { |
| + return widget_focused_ && view && focused_view_ == view; |
| +} |
| + |
| +void InputMethodWin::DispatchKeyEventPostIME(const KeyEvent& key) const { |
| + if (delegate_) |
| + delegate_->DispatchKeyEventPostIME(key); |
| +} |
| + |
| +void InputMethodWin::OnInputMethodChanged() const { |
| + TextInputClient* client = GetTextInputClient(); |
| + if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) |
| + client->OnInputMethodChanged(); |
| +} |
| + |
| +} // namespace views |