ddecd1db6a6f2a3ca752b851dea573fd778be5ac
[platform/framework/web/crosswalk.git] / src / ui / base / ime / input_method_win.cc
1 // Copyright (c) 2012 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 "ui/base/ime/input_method_win.h"
6
7 #include "base/basictypes.h"
8 #include "ui/base/ime/text_input_client.h"
9 #include "ui/events/event.h"
10 #include "ui/events/event_constants.h"
11 #include "ui/events/event_utils.h"
12 #include "ui/events/keycodes/keyboard_codes.h"
13 #include "ui/gfx/win/hwnd_util.h"
14
15 namespace ui {
16 namespace {
17
18 // Extra number of chars before and after selection (or composition) range which
19 // is returned to IME for improving conversion accuracy.
20 static const size_t kExtraNumberOfChars = 20;
21
22 }  // namespace
23
24 InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate,
25                                HWND toplevel_window_handle)
26     : active_(false),
27       toplevel_window_handle_(toplevel_window_handle),
28       pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION),
29       accept_carriage_return_(false) {
30   SetDelegate(delegate);
31 }
32
33 void InputMethodWin::Init(bool focused) {
34   // Gets the initial input locale.
35   OnInputLocaleChanged();
36
37   InputMethodBase::Init(focused);
38 }
39
40 bool InputMethodWin::DispatchKeyEvent(const ui::KeyEvent& event) {
41   if (!event.HasNativeEvent())
42     return DispatchFabricatedKeyEvent(event);
43
44   const base::NativeEvent& native_key_event = event.native_event();
45   if (native_key_event.message == WM_CHAR) {
46     BOOL handled;
47     OnChar(native_key_event.hwnd, native_key_event.message,
48            native_key_event.wParam, native_key_event.lParam, &handled);
49     return !!handled;  // Don't send WM_CHAR for post event processing.
50   }
51   // Handles ctrl-shift key to change text direction and layout alignment.
52   if (ui::IMM32Manager::IsRTLKeyboardLayoutInstalled() &&
53       !IsTextInputTypeNone()) {
54     // TODO: shouldn't need to generate a KeyEvent here.
55     const ui::KeyEvent key(native_key_event,
56                            native_key_event.message == WM_CHAR);
57     ui::KeyboardCode code = key.key_code();
58     if (key.type() == ui::ET_KEY_PRESSED) {
59       if (code == ui::VKEY_SHIFT) {
60         base::i18n::TextDirection dir;
61         if (ui::IMM32Manager::IsCtrlShiftPressed(&dir))
62           pending_requested_direction_ = dir;
63       } else if (code != ui::VKEY_CONTROL) {
64         pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION;
65       }
66     } else if (key.type() == ui::ET_KEY_RELEASED &&
67                (code == ui::VKEY_SHIFT || code == ui::VKEY_CONTROL) &&
68                pending_requested_direction_ != base::i18n::UNKNOWN_DIRECTION) {
69       GetTextInputClient()->ChangeTextDirectionAndLayoutAlignment(
70           pending_requested_direction_);
71       pending_requested_direction_ = base::i18n::UNKNOWN_DIRECTION;
72     }
73   }
74
75   return DispatchKeyEventPostIME(event);
76 }
77
78 void InputMethodWin::OnInputLocaleChanged() {
79   active_ = imm32_manager_.SetInputLanguage();
80   locale_ = imm32_manager_.GetInputLanguageName();
81   OnInputMethodChanged();
82 }
83
84 std::string InputMethodWin::GetInputLocale() {
85   return locale_;
86 }
87
88 bool InputMethodWin::IsActive() {
89   return active_;
90 }
91
92 void InputMethodWin::OnDidChangeFocusedClient(
93     TextInputClient* focused_before,
94     TextInputClient* focused) {
95   if (focused_before != focused)
96     accept_carriage_return_ = false;
97 }
98
99 LRESULT InputMethodWin::OnImeRequest(UINT message,
100                                      WPARAM wparam,
101                                      LPARAM lparam,
102                                      BOOL* handled) {
103   *handled = FALSE;
104
105   // Should not receive WM_IME_REQUEST message, if IME is disabled.
106   const ui::TextInputType type = GetTextInputType();
107   if (type == ui::TEXT_INPUT_TYPE_NONE ||
108       type == ui::TEXT_INPUT_TYPE_PASSWORD) {
109     return 0;
110   }
111
112   switch (wparam) {
113     case IMR_RECONVERTSTRING:
114       *handled = TRUE;
115       return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam));
116     case IMR_DOCUMENTFEED:
117       *handled = TRUE;
118       return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam));
119     case IMR_QUERYCHARPOSITION:
120       *handled = TRUE;
121       return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam));
122     default:
123       return 0;
124   }
125 }
126
127 LRESULT InputMethodWin::OnChar(HWND window_handle,
128                                UINT message,
129                                WPARAM wparam,
130                                LPARAM lparam,
131                                BOOL* handled) {
132   *handled = TRUE;
133
134   // We need to send character events to the focused text input client event if
135   // its text input type is ui::TEXT_INPUT_TYPE_NONE.
136   if (GetTextInputClient()) {
137     const base::char16 kCarriageReturn = L'\r';
138     const base::char16 ch = static_cast<base::char16>(wparam);
139     // A mask to determine the previous key state from |lparam|. The value is 1
140     // if the key is down before the message is sent, or it is 0 if the key is
141     // up.
142     const uint32 kPrevKeyDownBit = 0x40000000;
143     if (ch == kCarriageReturn && !(lparam & kPrevKeyDownBit))
144       accept_carriage_return_ = true;
145     // Conditionally ignore '\r' events to work around crbug.com/319100.
146     // TODO(yukawa, IME): Figure out long-term solution.
147     if (ch != kCarriageReturn || accept_carriage_return_)
148       GetTextInputClient()->InsertChar(ch, ui::GetModifiersFromKeyState());
149   }
150
151   // Explicitly show the system menu at a good location on [Alt]+[Space].
152   // Note: Setting |handled| to FALSE for DefWindowProc triggering of the system
153   //       menu causes undesirable titlebar artifacts in the classic theme.
154   if (message == WM_SYSCHAR && wparam == VK_SPACE)
155     gfx::ShowSystemMenu(window_handle);
156
157   return 0;
158 }
159
160 LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) {
161   ui::TextInputClient* client = GetTextInputClient();
162   if (!client)
163     return 0;
164
165   gfx::Range text_range;
166   if (!client->GetTextRange(&text_range) || text_range.is_empty())
167     return 0;
168
169   bool result = false;
170   gfx::Range target_range;
171   if (client->HasCompositionText())
172     result = client->GetCompositionTextRange(&target_range);
173
174   if (!result || target_range.is_empty()) {
175     if (!client->GetSelectionRange(&target_range) ||
176         !target_range.IsValid()) {
177       return 0;
178     }
179   }
180
181   if (!text_range.Contains(target_range))
182     return 0;
183
184   if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars)
185     text_range.set_start(target_range.GetMin() - kExtraNumberOfChars);
186
187   if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars)
188     text_range.set_end(target_range.GetMax() + kExtraNumberOfChars);
189
190   size_t len = text_range.length();
191   size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
192
193   if (!reconv)
194     return need_size;
195
196   if (reconv->dwSize < need_size)
197     return 0;
198
199   base::string16 text;
200   if (!GetTextInputClient()->GetTextFromRange(text_range, &text))
201     return 0;
202   DCHECK_EQ(text_range.length(), text.length());
203
204   reconv->dwVersion = 0;
205   reconv->dwStrLen = len;
206   reconv->dwStrOffset = sizeof(RECONVERTSTRING);
207   reconv->dwCompStrLen =
208       client->HasCompositionText() ? target_range.length() : 0;
209   reconv->dwCompStrOffset =
210       (target_range.GetMin() - text_range.start()) * sizeof(WCHAR);
211   reconv->dwTargetStrLen = target_range.length();
212   reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
213
214   memcpy((char*)reconv + sizeof(RECONVERTSTRING),
215          text.c_str(), len * sizeof(WCHAR));
216
217   // According to Microsoft API document, IMR_RECONVERTSTRING and
218   // IMR_DOCUMENTFEED should return reconv, but some applications return
219   // need_size.
220   return reinterpret_cast<LRESULT>(reconv);
221 }
222
223 LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) {
224   ui::TextInputClient* client = GetTextInputClient();
225   if (!client)
226     return 0;
227
228   // If there is a composition string already, we don't allow reconversion.
229   if (client->HasCompositionText())
230     return 0;
231
232   gfx::Range text_range;
233   if (!client->GetTextRange(&text_range) || text_range.is_empty())
234     return 0;
235
236   gfx::Range selection_range;
237   if (!client->GetSelectionRange(&selection_range) ||
238       selection_range.is_empty()) {
239     return 0;
240   }
241
242   DCHECK(text_range.Contains(selection_range));
243
244   size_t len = selection_range.length();
245   size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);
246
247   if (!reconv)
248     return need_size;
249
250   if (reconv->dwSize < need_size)
251     return 0;
252
253   // TODO(penghuang): Return some extra context to help improve IME's
254   // reconversion accuracy.
255   base::string16 text;
256   if (!GetTextInputClient()->GetTextFromRange(selection_range, &text))
257     return 0;
258   DCHECK_EQ(selection_range.length(), text.length());
259
260   reconv->dwVersion = 0;
261   reconv->dwStrLen = len;
262   reconv->dwStrOffset = sizeof(RECONVERTSTRING);
263   reconv->dwCompStrLen = len;
264   reconv->dwCompStrOffset = 0;
265   reconv->dwTargetStrLen = len;
266   reconv->dwTargetStrOffset = 0;
267
268   memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING),
269          text.c_str(), len * sizeof(WCHAR));
270
271   // According to Microsoft API document, IMR_RECONVERTSTRING and
272   // IMR_DOCUMENTFEED should return reconv, but some applications return
273   // need_size.
274   return reinterpret_cast<LRESULT>(reconv);
275 }
276
277 LRESULT InputMethodWin::OnQueryCharPosition(IMECHARPOSITION* char_positon) {
278   if (!char_positon)
279     return 0;
280
281   if (char_positon->dwSize < sizeof(IMECHARPOSITION))
282     return 0;
283
284   ui::TextInputClient* client = GetTextInputClient();
285   if (!client)
286     return 0;
287
288   gfx::Rect rect;
289   if (client->HasCompositionText()) {
290     if (!client->GetCompositionCharacterBounds(char_positon->dwCharPos,
291                                                &rect)) {
292       return 0;
293     }
294   } else {
295     // If there is no composition and the first character is queried, returns
296     // the caret bounds. This behavior is the same to that of RichEdit control.
297     if (char_positon->dwCharPos != 0)
298       return 0;
299     rect = client->GetCaretBounds();
300   }
301
302   char_positon->pt.x = rect.x();
303   char_positon->pt.y = rect.y();
304   char_positon->cLineHeight = rect.height();
305   return 1;  // returns non-zero value when succeeded.
306 }
307
308 HWND InputMethodWin::GetAttachedWindowHandle(
309   const TextInputClient* text_input_client) const {
310   // On Aura environment, we can assume that |toplevel_window_handle_| always
311   // represents the valid top-level window handle because each top-level window
312   // is responsible for lifecycle management of corresponding InputMethod
313   // instance.
314   return toplevel_window_handle_;
315 }
316
317 bool InputMethodWin::IsWindowFocused(const TextInputClient* client) const {
318   if (!client)
319     return false;
320   HWND attached_window_handle = GetAttachedWindowHandle(client);
321   // When Aura is enabled, |attached_window_handle| should always be a top-level
322   // window. So we can safely assume that |attached_window_handle| is ready for
323   // receiving keyboard input as long as it is an active window. This works well
324   // even when the |attached_window_handle| becomes active but has not received
325   // WM_FOCUS yet.
326   return attached_window_handle && GetActiveWindow() == attached_window_handle;
327 }
328
329 bool InputMethodWin::DispatchFabricatedKeyEvent(const ui::KeyEvent& event) {
330   if (event.is_char()) {
331     if (GetTextInputClient()) {
332       GetTextInputClient()->InsertChar(event.key_code(),
333                                        ui::GetModifiersFromKeyState());
334       return true;
335     }
336   }
337   return DispatchKeyEventPostIME(event);
338 }
339
340 }  // namespace ui