Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1219)

Side by Side Diff: views/ime/input_method_win.cc

Issue 8581003: views: Move ime and test directories to ui/views. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix linux Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/ime/input_method_win.h ('k') | views/ime/mock_input_method.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/ime/composition_text.h"
11 #include "ui/base/ime/text_input_client.h"
12 #include "ui/base/keycodes/keyboard_codes.h"
13 #include "views/events/event.h"
14
15 // Extra number of chars before and after selection (or composition) range which
16 // is returned to IME for improving conversion accuracy.
17 static const size_t kExtraNumberOfChars = 20;
18
19 namespace views {
20
21 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate)
22 : active_(false),
23 direction_(base::i18n::UNKNOWN_DIRECTION),
24 pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION) {
25 set_delegate(delegate);
26 }
27
28 InputMethodWin::~InputMethodWin() {
29 if (widget())
30 ime_input_.DisableIME(hwnd());
31 }
32
33 void InputMethodWin::Init(Widget* widget) {
34 // Gets the initial input locale and text direction information.
35 OnInputLangChange(0, 0);
36
37 InputMethodBase::Init(widget);
38 }
39
40 void InputMethodWin::OnFocus() {
41 DCHECK(!widget_focused());
42 InputMethodBase::OnFocus();
43 UpdateIMEState();
44 }
45
46 void InputMethodWin::OnBlur() {
47 DCHECK(widget_focused());
48 ConfirmCompositionText();
49 InputMethodBase::OnBlur();
50 }
51
52 void InputMethodWin::DispatchKeyEvent(const KeyEvent& key) {
53 // Handles ctrl-shift key to change text direction and layout alignment.
54 if (ui::ImeInput::IsRTLKeyboardLayoutInstalled() && !IsTextInputTypeNone()) {
55 ui::KeyboardCode code = key.key_code();
56 if (key.type() == ui::ET_KEY_PRESSED) {
57 if (code == ui::VKEY_SHIFT) {
58 base::i18n::TextDirection dir;
59 if (ui::ImeInput::IsCtrlShiftPressed(&dir))
60 pending_requested_direction_ = dir;
61 } else if (code != ui::VKEY_CONTROL) {
62 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION;
63 }
64 } else if (key.type() == ui::ET_KEY_RELEASED &&
65 (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) &&
66 pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) {
67 GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment(
68 pending_requested_direction_);
69 pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION;
70 }
71 }
72
73 DispatchKeyEventPostIME(key);
74 }
75
76 void InputMethodWin::OnTextInputTypeChanged(View* view) {
77 if (IsViewFocused(view)) {
78 ime_input_.CancelIME(hwnd());
79 UpdateIMEState();
80 }
81 InputMethodBase::OnTextInputTypeChanged(view);
82 }
83
84 void InputMethodWin::OnCaretBoundsChanged(View* view) {
85 gfx::Rect rect;
86 if (!IsViewFocused(view) || !GetCaretBoundsInWidget(&rect))
87 return;
88 ime_input_.UpdateCaretRect(hwnd(), rect);
89 }
90
91 void InputMethodWin::CancelComposition(View* view) {
92 if (IsViewFocused(view))
93 ime_input_.CancelIME(hwnd());
94 }
95
96 std::string InputMethodWin::GetInputLocale() {
97 return locale_;
98 }
99
100 base::i18n::TextDirection InputMethodWin::GetInputTextDirection() {
101 return direction_;
102 }
103
104 bool InputMethodWin::IsActive() {
105 return active_;
106 }
107
108 LRESULT InputMethodWin::OnImeMessages(
109 UINT message, WPARAM w_param, LPARAM l_param, BOOL* handled) {
110 LRESULT result = 0;
111 switch (message) {
112 case WM_IME_SETCONTEXT:
113 result = OnImeSetContext(message, w_param, l_param, handled);
114 break;
115 case WM_IME_STARTCOMPOSITION:
116 result = OnImeStartComposition(message, w_param, l_param, handled);
117 break;
118 case WM_IME_COMPOSITION:
119 result = OnImeComposition(message, w_param, l_param, handled);
120 break;
121 case WM_IME_ENDCOMPOSITION:
122 result = OnImeEndComposition(message, w_param, l_param, handled);
123 break;
124 case WM_IME_REQUEST:
125 result = OnImeRequest(message, w_param, l_param, handled);
126 break;
127 case WM_CHAR:
128 case WM_SYSCHAR:
129 result = OnChar(message, w_param, l_param, handled);
130 break;
131 case WM_DEADCHAR:
132 case WM_SYSDEADCHAR:
133 result = OnDeadChar(message, w_param, l_param, handled);
134 break;
135 default:
136 NOTREACHED() << "Unknown IME message:" << message;
137 break;
138 }
139 return result;
140 }
141
142 void InputMethodWin::OnWillChangeFocus(View* focused_before, View* focused) {
143 ConfirmCompositionText();
144 }
145
146 void InputMethodWin::OnDidChangeFocus(View* focused_before, View* focused) {
147 UpdateIMEState();
148 }
149
150 void InputMethodWin::OnInputLangChange(DWORD character_set,
151 HKL input_language_id) {
152 active_ = ime_input_.SetInputLanguage();
153 locale_ = ime_input_.GetInputLanguageName();
154 direction_ = ime_input_.GetTextDirection();
155 OnInputMethodChanged();
156 }
157
158 LRESULT InputMethodWin::OnImeSetContext(
159 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
160 active_ = (wparam == TRUE);
161 if (active_)
162 ime_input_.CreateImeWindow(hwnd());
163
164 OnInputMethodChanged();
165 return ime_input_.SetImeWindowStyle(hwnd(), message, wparam, lparam, handled);
166 }
167
168 LRESULT InputMethodWin::OnImeStartComposition(
169 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
170 // We have to prevent WTL from calling ::DefWindowProc() because the function
171 // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to
172 // over-write the position of IME windows.
173 *handled = TRUE;
174
175 if (IsTextInputTypeNone())
176 return 0;
177
178 // Reset the composition status and create IME windows.
179 ime_input_.CreateImeWindow(hwnd());
180 ime_input_.ResetComposition(hwnd());
181 return 0;
182 }
183
184 LRESULT InputMethodWin::OnImeComposition(
185 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
186 // We have to prevent WTL from calling ::DefWindowProc() because we do not
187 // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
188 *handled = TRUE;
189
190 if (IsTextInputTypeNone())
191 return 0;
192
193 // At first, update the position of the IME window.
194 ime_input_.UpdateImeWindow(hwnd());
195
196 // Retrieve the result string and its attributes of the ongoing composition
197 // and send it to a renderer process.
198 ui::CompositionText composition;
199 if (ime_input_.GetResult(hwnd(), lparam, &composition.text)) {
200 GetTextInputClient()->InsertText(composition.text);
201 ime_input_.ResetComposition(hwnd());
202 // Fall though and try reading the composition string.
203 // Japanese IMEs send a message containing both GCS_RESULTSTR and
204 // GCS_COMPSTR, which means an ongoing composition has been finished
205 // by the start of another composition.
206 }
207 // Retrieve the composition string and its attributes of the ongoing
208 // composition and send it to a renderer process.
209 if (ime_input_.GetComposition(hwnd(), lparam, &composition))
210 GetTextInputClient()->SetCompositionText(composition);
211
212 return 0;
213 }
214
215 LRESULT InputMethodWin::OnImeEndComposition(
216 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
217 // Let WTL call ::DefWindowProc() and release its resources.
218 *handled = FALSE;
219
220 if (IsTextInputTypeNone())
221 return 0;
222
223 if (GetTextInputClient()->HasCompositionText())
224 GetTextInputClient()->ClearCompositionText();
225
226 ime_input_.ResetComposition(hwnd());
227 ime_input_.DestroyImeWindow(hwnd());
228 return 0;
229 }
230
231 LRESULT InputMethodWin::OnImeRequest(
232 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
233 *handled = FALSE;
234
235 // Should not receive WM_IME_REQUEST message, if IME is disabled.
236 const ui::TextInputType type = GetTextInputType();
237 if (type == ui::TEXT_INPUT_TYPE_NONE ||
238 type == ui::TEXT_INPUT_TYPE_PASSWORD) {
239 return 0;
240 }
241
242 switch (wparam) {
243 case IMR_RECONVERTSTRING:
244 *handled = TRUE;
245 return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam));
246 case IMR_DOCUMENTFEED:
247 *handled = TRUE;
248 return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam));
249 default:
250 return 0;
251 }
252 }
253
254 LRESULT InputMethodWin::OnChar(
255 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
256 *handled = TRUE;
257
258 // We need to send character events to the focused text input client event if
259 // its text input type is ui::TEXT_INPUT_TYPE_NONE.
260 if (!GetTextInputClient())
261 return 0;
262
263 int flags = 0;
264 flags |= (::GetKeyState(VK_MENU) & 0x80)? ui::EF_ALT_DOWN : 0;
265 flags |= (::GetKeyState(VK_SHIFT) & 0x80)? ui::EF_SHIFT_DOWN : 0;
266 flags |= (::GetKeyState(VK_CONTROL) & 0x80)? ui::EF_CONTROL_DOWN : 0;
267 GetTextInputClient()->InsertChar(static_cast<char16>(wparam), flags);
268 return 0;
269 }
270
271 LRESULT InputMethodWin::OnDeadChar(
272 UINT message, WPARAM wparam, LPARAM lparam, BOOL* handled) {
273 *handled = TRUE;
274
275 if (IsTextInputTypeNone())
276 return 0;
277
278 // Shows the dead character as a composition text, so that the user can know
279 // what dead key was pressed.
280 ui::CompositionText composition;
281 composition.text.assign(1, static_cast<char16>(wparam));
282 composition.selection = ui::Range(0, 1);
283 composition.underlines.push_back(
284 ui::CompositionUnderline(0, 1, SK_ColorBLACK, false));
285 GetTextInputClient()->SetCompositionText(composition);
286 return 0;
287 }
288
289 LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) {
290 ui::TextInputClient* client = GetTextInputClient();
291 if (!client)
292 return 0;
293
294 ui::Range text_range;
295 if (!client->GetTextRange(&text_range) || text_range.is_empty())
296 return 0;
297
298 bool result = false;
299 ui::Range target_range;
300 if (client->HasCompositionText())
301 result = client->GetCompositionTextRange(&target_range);
302
303 if (!result || target_range.is_empty()) {
304 if (!client->GetSelectionRange(&target_range) ||
305 !target_range.IsValid()) {
306 return 0;
307 }
308 }
309
310 if (!text_range.Contains(target_range))
311 return 0;
312
313 if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars)
314 text_range.set_start(target_range.GetMin() - kExtraNumberOfChars);
315
316 if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars)
317 text_range.set_end(target_range.GetMax() + kExtraNumberOfChars);
318
319 size_t len = text_range.length();
320 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
321
322 if (!reconv)
323 return need_size;
324
325 if (reconv->dwSize < need_size)
326 return 0;
327
328 string16 text;
329 if (!GetTextInputClient()->GetTextFromRange(text_range, &text))
330 return 0;
331 DCHECK_EQ(text_range.length(), text.length());
332
333 reconv->dwVersion = 0;
334 reconv->dwStrLen = len;
335 reconv->dwStrOffset = sizeof(RECONVERTSTRING);
336 reconv->dwCompStrLen =
337 client->HasCompositionText() ? target_range.length() : 0;
338 reconv->dwCompStrOffset =
339 (target_range.GetMin() - text_range.start()) * sizeof(WCHAR);
340 reconv->dwTargetStrLen = target_range.length();
341 reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
342
343 memcpy((char*)reconv + sizeof(RECONVERTSTRING),
344 text.c_str(), len * sizeof(WCHAR));
345
346 // According to Microsft API document, IMR_RECONVERTSTRING and
347 // IMR_DOCUMENTFEED should return reconv, but some applications return
348 // need_size.
349 return reinterpret_cast<LRESULT>(reconv);
350 }
351
352 LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) {
353 ui::TextInputClient* client = GetTextInputClient();
354 if (!client)
355 return 0;
356
357 // If there is a composition string already, we don't allow reconversion.
358 if (client->HasCompositionText())
359 return 0;
360
361 ui::Range text_range;
362 if (!client->GetTextRange(&text_range) || text_range.is_empty())
363 return 0;
364
365 ui::Range selection_range;
366 if (!client->GetSelectionRange(&selection_range) ||
367 selection_range.is_empty()) {
368 return 0;
369 }
370
371 DCHECK(text_range.Contains(selection_range));
372
373 size_t len = selection_range.length();
374 size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
375
376 if (!reconv)
377 return need_size;
378
379 if (reconv->dwSize < need_size)
380 return 0;
381
382 // TODO(penghuang): Return some extra context to help improve IME's
383 // reconversion accuracy.
384 string16 text;
385 if (!GetTextInputClient()->GetTextFromRange(selection_range, &text))
386 return 0;
387 DCHECK_EQ(selection_range.length(), text.length());
388
389 reconv->dwVersion = 0;
390 reconv->dwStrLen = len;
391 reconv->dwStrOffset = sizeof(RECONVERTSTRING);
392 reconv->dwCompStrLen = len;
393 reconv->dwCompStrOffset = 0;
394 reconv->dwTargetStrLen = len;
395 reconv->dwTargetStrOffset = 0;
396
397 memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
398 text.c_str(), len * sizeof(WCHAR));
399
400 // According to Microsft API document, IMR_RECONVERTSTRING and
401 // IMR_DOCUMENTFEED should return reconv, but some applications return
402 // need_size.
403 return reinterpret_cast<LRESULT>(reconv);
404 }
405
406 void InputMethodWin::ConfirmCompositionText() {
407 if (!IsTextInputTypeNone()) {
408 ime_input_.CleanupComposition(hwnd());
409 // Though above line should confirm the client's composition text by sending
410 // a result text to us, in case the input method and the client are in
411 // inconsistent states, we check the client's composition state again.
412 if (GetTextInputClient()->HasCompositionText())
413 GetTextInputClient()->ConfirmCompositionText();
414 }
415 }
416
417 void InputMethodWin::UpdateIMEState() {
418 // Use switch here in case we are going to add more text input types.
419 // We disable input method in password field.
420 switch (GetTextInputType()) {
421 case ui::TEXT_INPUT_TYPE_NONE:
422 case ui::TEXT_INPUT_TYPE_PASSWORD:
423 ime_input_.DisableIME(hwnd());
424 break;
425 default:
426 ime_input_.EnableIME(hwnd());
427 break;
428 }
429 }
430
431 } // namespace views
OLDNEW
« no previous file with comments | « views/ime/input_method_win.h ('k') | views/ime/mock_input_method.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698