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 |