Home | History | Annotate | Download | only in ime
      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.
      4 
      5 #include "ui/base/ime/input_method_imm32.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "ui/base/ime/composition_text.h"
      9 #include "ui/base/ime/text_input_client.h"
     10 
     11 
     12 namespace ui {
     13 
     14 InputMethodIMM32::InputMethodIMM32(internal::InputMethodDelegate* delegate,
     15                                    HWND toplevel_window_handle)
     16     : InputMethodWin(delegate, toplevel_window_handle),
     17       enabled_(false), is_candidate_popup_open_(false),
     18       composing_window_handle_(NULL) {
     19   // In non-Aura environment, appropriate callbacks to OnFocus() and OnBlur()
     20   // are not implemented yet. To work around this limitation, here we use
     21   // "always focused" model.
     22   // TODO(ime): Fix the caller of OnFocus() and OnBlur() so that appropriate
     23   // focus event will be passed.
     24   InputMethodWin::OnFocus();
     25 }
     26 
     27 void InputMethodIMM32::OnFocus() {
     28   // Ignore OnFocus event for "always focused" model. See the comment in the
     29   // constructor.
     30   // TODO(ime): Implement OnFocus once the callers are fixed.
     31 }
     32 
     33 void InputMethodIMM32::OnBlur() {
     34   // Ignore OnBlur event for "always focused" model. See the comment in the
     35   // constructor.
     36   // TODO(ime): Implement OnFocus once the callers are fixed.
     37 }
     38 
     39 bool InputMethodIMM32::OnUntranslatedIMEMessage(
     40     const base::NativeEvent& event, InputMethod::NativeEventResult* result) {
     41   LRESULT original_result = 0;
     42   BOOL handled = FALSE;
     43   switch (event.message) {
     44     case WM_IME_SETCONTEXT:
     45       original_result = OnImeSetContext(
     46           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     47       break;
     48     case WM_IME_STARTCOMPOSITION:
     49       original_result = OnImeStartComposition(
     50           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     51       break;
     52     case WM_IME_COMPOSITION:
     53       original_result = OnImeComposition(
     54           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     55       break;
     56     case WM_IME_ENDCOMPOSITION:
     57       original_result = OnImeEndComposition(
     58           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     59       break;
     60     case WM_IME_REQUEST:
     61       original_result = OnImeRequest(
     62           event.message, event.wParam, event.lParam, &handled);
     63       break;
     64     case WM_CHAR:
     65     case WM_SYSCHAR:
     66       original_result = OnChar(
     67           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     68       break;
     69     case WM_DEADCHAR:
     70     case WM_SYSDEADCHAR:
     71       original_result = OnDeadChar(
     72           event.message, event.wParam, event.lParam, &handled);
     73       break;
     74     case WM_IME_NOTIFY:
     75       original_result = OnImeNotify(
     76           event.message, event.wParam, event.lParam, &handled);
     77       break;
     78     default:
     79       NOTREACHED() << "Unknown IME message:" << event.message;
     80       break;
     81   }
     82   if (result)
     83     *result = original_result;
     84   return !!handled;
     85 }
     86 
     87 void InputMethodIMM32::OnTextInputTypeChanged(const TextInputClient* client) {
     88   if (IsTextInputClientFocused(client) && IsWindowFocused(client)) {
     89     imm32_manager_.CancelIME(GetAttachedWindowHandle(client));
     90     UpdateIMEState();
     91   }
     92   InputMethodWin::OnTextInputTypeChanged(client);
     93 }
     94 
     95 void InputMethodIMM32::OnCaretBoundsChanged(const TextInputClient* client) {
     96   if (!enabled_ || !IsTextInputClientFocused(client) ||
     97       !IsWindowFocused(client)) {
     98     return;
     99   }
    100 
    101   // The current text input type should not be NONE if |client| is focused.
    102   DCHECK(!IsTextInputTypeNone());
    103   gfx::Rect screen_bounds(GetTextInputClient()->GetCaretBounds());
    104 
    105   HWND attached_window = GetAttachedWindowHandle(client);
    106   // TODO(ime): see comment in TextInputClient::GetCaretBounds(), this
    107   // conversion shouldn't be necessary.
    108   RECT r;
    109   GetClientRect(attached_window, &r);
    110   POINT window_point = { screen_bounds.x(), screen_bounds.y() };
    111   ScreenToClient(attached_window, &window_point);
    112   imm32_manager_.UpdateCaretRect(
    113       attached_window,
    114       gfx::Rect(gfx::Point(window_point.x, window_point.y),
    115                 screen_bounds.size()));
    116 }
    117 
    118 void InputMethodIMM32::CancelComposition(const TextInputClient* client) {
    119   if (enabled_ && IsTextInputClientFocused(client))
    120     imm32_manager_.CancelIME(GetAttachedWindowHandle(client));
    121 }
    122 
    123 void InputMethodIMM32::SetFocusedTextInputClient(TextInputClient* client) {
    124   ConfirmCompositionText();
    125   InputMethodWin::SetFocusedTextInputClient(client);
    126 }
    127 
    128 bool InputMethodIMM32::IsCandidatePopupOpen() const {
    129   return is_candidate_popup_open_;
    130 }
    131 
    132 void InputMethodIMM32::OnWillChangeFocusedClient(
    133     TextInputClient* focused_before,
    134     TextInputClient* focused) {
    135   if (IsWindowFocused(focused_before)) {
    136     ConfirmCompositionText();
    137   }
    138 }
    139 
    140 void InputMethodIMM32::OnDidChangeFocusedClient(TextInputClient* focused_before,
    141                                                 TextInputClient* focused) {
    142   if (IsWindowFocused(focused)) {
    143     // Force to update the input type since client's TextInputStateChanged()
    144     // function might not be called if text input types before the client loses
    145     // focus and after it acquires focus again are the same.
    146     OnTextInputTypeChanged(focused);
    147 
    148     UpdateIMEState();
    149 
    150     // Force to update caret bounds, in case the client thinks that the caret
    151     // bounds has not changed.
    152     OnCaretBoundsChanged(focused);
    153   }
    154 }
    155 
    156 LRESULT InputMethodIMM32::OnImeSetContext(HWND window_handle,
    157                                           UINT message,
    158                                           WPARAM wparam,
    159                                           LPARAM lparam,
    160                                           BOOL* handled) {
    161   active_ = (wparam == TRUE);
    162   if (active_)
    163     imm32_manager_.CreateImeWindow(window_handle);
    164 
    165   OnInputMethodChanged();
    166   return imm32_manager_.SetImeWindowStyle(
    167       window_handle, message, wparam, lparam, handled);
    168 }
    169 
    170 LRESULT InputMethodIMM32::OnImeStartComposition(HWND window_handle,
    171                                                 UINT message,
    172                                                 WPARAM wparam,
    173                                                 LPARAM lparam,
    174                                                 BOOL* handled) {
    175   // We have to prevent WTL from calling ::DefWindowProc() because the function
    176   // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to
    177   // over-write the position of IME windows.
    178   *handled = TRUE;
    179 
    180   // Reset the composition status and create IME windows.
    181   composing_window_handle_ = window_handle;
    182   imm32_manager_.CreateImeWindow(window_handle);
    183   imm32_manager_.ResetComposition(window_handle);
    184   return 0;
    185 }
    186 
    187 LRESULT InputMethodIMM32::OnImeComposition(HWND window_handle,
    188                                            UINT message,
    189                                            WPARAM wparam,
    190                                            LPARAM lparam,
    191                                            BOOL* handled) {
    192   // We have to prevent WTL from calling ::DefWindowProc() because we do not
    193   // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages.
    194   *handled = TRUE;
    195 
    196   // At first, update the position of the IME window.
    197   imm32_manager_.UpdateImeWindow(window_handle);
    198 
    199   // Retrieve the result string and its attributes of the ongoing composition
    200   // and send it to a renderer process.
    201   ui::CompositionText composition;
    202   if (imm32_manager_.GetResult(window_handle, lparam, &composition.text)) {
    203     if (!IsTextInputTypeNone())
    204       GetTextInputClient()->InsertText(composition.text);
    205     imm32_manager_.ResetComposition(window_handle);
    206     // Fall though and try reading the composition string.
    207     // Japanese IMEs send a message containing both GCS_RESULTSTR and
    208     // GCS_COMPSTR, which means an ongoing composition has been finished
    209     // by the start of another composition.
    210   }
    211   // Retrieve the composition string and its attributes of the ongoing
    212   // composition and send it to a renderer process.
    213   if (imm32_manager_.GetComposition(window_handle, lparam, &composition) &&
    214       !IsTextInputTypeNone())
    215     GetTextInputClient()->SetCompositionText(composition);
    216 
    217   return 0;
    218 }
    219 
    220 LRESULT InputMethodIMM32::OnImeEndComposition(HWND window_handle,
    221                                               UINT message,
    222                                               WPARAM wparam,
    223                                               LPARAM lparam,
    224                                               BOOL* handled) {
    225   // Let WTL call ::DefWindowProc() and release its resources.
    226   *handled = FALSE;
    227 
    228   composing_window_handle_ = NULL;
    229 
    230   if (!IsTextInputTypeNone() && GetTextInputClient()->HasCompositionText())
    231     GetTextInputClient()->ClearCompositionText();
    232 
    233   imm32_manager_.ResetComposition(window_handle);
    234   imm32_manager_.DestroyImeWindow(window_handle);
    235   return 0;
    236 }
    237 
    238 LRESULT InputMethodIMM32::OnImeNotify(UINT message,
    239                                       WPARAM wparam,
    240                                       LPARAM lparam,
    241                                       BOOL* handled) {
    242   *handled = FALSE;
    243 
    244   // Update |is_candidate_popup_open_|, whether a candidate window is open.
    245   switch (wparam) {
    246   case IMN_OPENCANDIDATE:
    247     is_candidate_popup_open_ = true;
    248     break;
    249   case IMN_CLOSECANDIDATE:
    250     is_candidate_popup_open_ = false;
    251     break;
    252   }
    253 
    254   return 0;
    255 }
    256 
    257 void InputMethodIMM32::ConfirmCompositionText() {
    258   if (composing_window_handle_)
    259     imm32_manager_.CleanupComposition(composing_window_handle_);
    260 
    261   if (!IsTextInputTypeNone()) {
    262     // Though above line should confirm the client's composition text by sending
    263     // a result text to us, in case the input method and the client are in
    264     // inconsistent states, we check the client's composition state again.
    265     if (GetTextInputClient()->HasCompositionText())
    266       GetTextInputClient()->ConfirmCompositionText();
    267   }
    268 }
    269 
    270 void InputMethodIMM32::UpdateIMEState() {
    271   // Use switch here in case we are going to add more text input types.
    272   // We disable input method in password field.
    273   switch (GetTextInputType()) {
    274     case ui::TEXT_INPUT_TYPE_NONE:
    275     case ui::TEXT_INPUT_TYPE_PASSWORD:
    276       imm32_manager_.DisableIME(GetAttachedWindowHandle(GetTextInputClient()));
    277       enabled_ = false;
    278       break;
    279     default:
    280       imm32_manager_.EnableIME(GetAttachedWindowHandle(GetTextInputClient()));
    281       enabled_ = true;
    282       break;
    283   }
    284 }
    285 
    286 bool InputMethodIMM32::IsWindowFocused(const TextInputClient* client) const {
    287   if (!client)
    288     return false;
    289   HWND attached_window_handle = GetAttachedWindowHandle(client);
    290   return attached_window_handle && GetFocus() == attached_window_handle;
    291 }
    292 
    293 }  // namespace ui
    294