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 |