1 // Copyright (c) 2013 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.
5 #include "ui/base/ime/input_method_imm32.h"
7 #include "base/basictypes.h"
8 #include "ui/base/ime/composition_text.h"
9 #include "ui/base/ime/text_input_client.h"
10 #include "ui/base/ime/win/tsf_input_scope.h"
15 InputMethodIMM32::InputMethodIMM32(internal::InputMethodDelegate
* delegate
,
16 HWND toplevel_window_handle
)
17 : InputMethodWin(delegate
, toplevel_window_handle
),
18 enabled_(false), is_candidate_popup_open_(false),
19 composing_window_handle_(NULL
) {
20 // In non-Aura environment, appropriate callbacks to OnFocus() and OnBlur()
21 // are not implemented yet. To work around this limitation, here we use
22 // "always focused" model.
23 // TODO(ime): Fix the caller of OnFocus() and OnBlur() so that appropriate
24 // focus event will be passed.
25 InputMethodWin::OnFocus();
28 void InputMethodIMM32::OnFocus() {
29 // Ignore OnFocus event for "always focused" model. See the comment in the
31 // TODO(ime): Implement OnFocus once the callers are fixed.
34 void InputMethodIMM32::OnBlur() {
35 // Ignore OnBlur event for "always focused" model. See the comment in the
37 // TODO(ime): Implement OnFocus once the callers are fixed.
40 bool InputMethodIMM32::OnUntranslatedIMEMessage(
41 const base::NativeEvent
& event
, InputMethod::NativeEventResult
* result
) {
42 LRESULT original_result
= 0;
44 switch (event
.message
) {
45 case WM_IME_SETCONTEXT
:
46 original_result
= OnImeSetContext(
47 event
.hwnd
, event
.message
, event
.wParam
, event
.lParam
, &handled
);
49 case WM_IME_STARTCOMPOSITION
:
50 original_result
= OnImeStartComposition(
51 event
.hwnd
, event
.message
, event
.wParam
, event
.lParam
, &handled
);
53 case WM_IME_COMPOSITION
:
54 original_result
= OnImeComposition(
55 event
.hwnd
, event
.message
, event
.wParam
, event
.lParam
, &handled
);
57 case WM_IME_ENDCOMPOSITION
:
58 original_result
= OnImeEndComposition(
59 event
.hwnd
, event
.message
, event
.wParam
, event
.lParam
, &handled
);
62 original_result
= OnImeRequest(
63 event
.message
, event
.wParam
, event
.lParam
, &handled
);
67 original_result
= OnChar(
68 event
.hwnd
, event
.message
, event
.wParam
, event
.lParam
, &handled
);
71 original_result
= OnImeNotify(
72 event
.message
, event
.wParam
, event
.lParam
, &handled
);
75 NOTREACHED() << "Unknown IME message:" << event
.message
;
79 *result
= original_result
;
83 void InputMethodIMM32::OnTextInputTypeChanged(const TextInputClient
* client
) {
84 if (IsTextInputClientFocused(client
) && IsWindowFocused(client
)) {
85 imm32_manager_
.CancelIME(GetAttachedWindowHandle(client
));
88 InputMethodWin::OnTextInputTypeChanged(client
);
91 void InputMethodIMM32::OnCaretBoundsChanged(const TextInputClient
* client
) {
92 if (!enabled_
|| !IsTextInputClientFocused(client
) ||
93 !IsWindowFocused(client
)) {
96 // The current text input type should not be NONE if |client| is focused.
97 DCHECK(!IsTextInputTypeNone());
98 gfx::Rect
screen_bounds(GetTextInputClient()->GetCaretBounds());
100 HWND attached_window
= GetAttachedWindowHandle(client
);
101 // TODO(ime): see comment in TextInputClient::GetCaretBounds(), this
102 // conversion shouldn't be necessary.
104 GetClientRect(attached_window
, &r
);
105 POINT window_point
= { screen_bounds
.x(), screen_bounds
.y() };
106 ScreenToClient(attached_window
, &window_point
);
107 gfx::Rect
caret_rect(gfx::Point(window_point
.x
, window_point
.y
),
108 screen_bounds
.size());
109 imm32_manager_
.UpdateCaretRect(attached_window
, caret_rect
);
112 void InputMethodIMM32::CancelComposition(const TextInputClient
* client
) {
113 if (enabled_
&& IsTextInputClientFocused(client
))
114 imm32_manager_
.CancelIME(GetAttachedWindowHandle(client
));
117 bool InputMethodIMM32::IsCandidatePopupOpen() const {
118 return is_candidate_popup_open_
;
121 void InputMethodIMM32::OnWillChangeFocusedClient(
122 TextInputClient
* focused_before
,
123 TextInputClient
* focused
) {
124 if (IsWindowFocused(focused_before
)) {
125 ConfirmCompositionText();
129 void InputMethodIMM32::OnDidChangeFocusedClient(TextInputClient
* focused_before
,
130 TextInputClient
* focused
) {
131 if (IsWindowFocused(focused
)) {
132 // Force to update the input type since client's TextInputStateChanged()
133 // function might not be called if text input types before the client loses
134 // focus and after it acquires focus again are the same.
135 OnTextInputTypeChanged(focused
);
139 // Force to update caret bounds, in case the client thinks that the caret
140 // bounds has not changed.
141 OnCaretBoundsChanged(focused
);
143 InputMethodWin::OnDidChangeFocusedClient(focused_before
, focused
);
146 LRESULT
InputMethodIMM32::OnImeSetContext(HWND window_handle
,
152 imm32_manager_
.CreateImeWindow(window_handle
);
154 OnInputMethodChanged();
155 return imm32_manager_
.SetImeWindowStyle(
156 window_handle
, message
, wparam
, lparam
, handled
);
159 LRESULT
InputMethodIMM32::OnImeStartComposition(HWND window_handle
,
164 // We have to prevent WTL from calling ::DefWindowProc() because the function
165 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to
166 // over-write the position of IME windows.
169 // Reset the composition status and create IME windows.
170 composing_window_handle_
= window_handle
;
171 imm32_manager_
.CreateImeWindow(window_handle
);
172 imm32_manager_
.ResetComposition(window_handle
);
176 LRESULT
InputMethodIMM32::OnImeComposition(HWND window_handle
,
181 // We have to prevent WTL from calling ::DefWindowProc() because we do not
182 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
185 // At first, update the position of the IME window.
186 imm32_manager_
.UpdateImeWindow(window_handle
);
188 // Retrieve the result string and its attributes of the ongoing composition
189 // and send it to a renderer process.
190 ui::CompositionText composition
;
191 if (imm32_manager_
.GetResult(window_handle
, lparam
, &composition
.text
)) {
192 if (!IsTextInputTypeNone())
193 GetTextInputClient()->InsertText(composition
.text
);
194 imm32_manager_
.ResetComposition(window_handle
);
195 // Fall though and try reading the composition string.
196 // Japanese IMEs send a message containing both GCS_RESULTSTR and
197 // GCS_COMPSTR, which means an ongoing composition has been finished
198 // by the start of another composition.
200 // Retrieve the composition string and its attributes of the ongoing
201 // composition and send it to a renderer process.
202 if (imm32_manager_
.GetComposition(window_handle
, lparam
, &composition
) &&
203 !IsTextInputTypeNone())
204 GetTextInputClient()->SetCompositionText(composition
);
209 LRESULT
InputMethodIMM32::OnImeEndComposition(HWND window_handle
,
214 // Let WTL call ::DefWindowProc() and release its resources.
217 composing_window_handle_
= NULL
;
219 if (!IsTextInputTypeNone() && GetTextInputClient()->HasCompositionText())
220 GetTextInputClient()->ClearCompositionText();
222 imm32_manager_
.ResetComposition(window_handle
);
223 imm32_manager_
.DestroyImeWindow(window_handle
);
227 LRESULT
InputMethodIMM32::OnImeNotify(UINT message
,
233 bool previous_state
= is_candidate_popup_open_
;
235 // Update |is_candidate_popup_open_|, whether a candidate window is open.
237 case IMN_OPENCANDIDATE
:
238 is_candidate_popup_open_
= true;
240 OnCandidateWindowShown();
242 case IMN_CLOSECANDIDATE
:
243 is_candidate_popup_open_
= false;
245 OnCandidateWindowHidden();
247 case IMN_CHANGECANDIDATE
:
248 // TODO(kochi): The IME API expects this event to notify window size change,
249 // while this may fire more often without window resize. There is no generic
250 // way to get bounds of candidate window.
251 OnCandidateWindowUpdated();
258 void InputMethodIMM32::ConfirmCompositionText() {
259 if (composing_window_handle_
)
260 imm32_manager_
.CleanupComposition(composing_window_handle_
);
262 if (!IsTextInputTypeNone()) {
263 // Though above line should confirm the client's composition text by sending
264 // a result text to us, in case the input method and the client are in
265 // inconsistent states, we check the client's composition state again.
266 if (GetTextInputClient()->HasCompositionText())
267 GetTextInputClient()->ConfirmCompositionText();
271 void InputMethodIMM32::UpdateIMEState() {
272 // Use switch here in case we are going to add more text input types.
273 // We disable input method in password field.
274 const HWND window_handle
= GetAttachedWindowHandle(GetTextInputClient());
275 const TextInputType text_input_type
= GetTextInputType();
276 const TextInputMode text_input_mode
= GetTextInputMode();
277 switch (text_input_type
) {
278 case ui::TEXT_INPUT_TYPE_NONE
:
279 case ui::TEXT_INPUT_TYPE_PASSWORD
:
280 imm32_manager_
.DisableIME(window_handle
);
284 imm32_manager_
.EnableIME(window_handle
);
289 imm32_manager_
.SetTextInputMode(window_handle
, text_input_mode
);
290 tsf_inputscope::SetInputScopeForTsfUnawareWindow(
291 window_handle
, text_input_type
, text_input_mode
);