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_tsf.h"
      6 
      7 #include "ui/base/ime/text_input_client.h"
      8 #include "ui/base/ime/win/tsf_bridge.h"
      9 #include "ui/base/ime/win/tsf_event_router.h"
     10 
     11 namespace ui {
     12 
     13 class InputMethodTSF::TSFEventObserver : public TSFEventRouterObserver {
     14  public:
     15   TSFEventObserver() : is_candidate_popup_open_(false) {}
     16 
     17   // Returns true if we know for sure that a candidate window (or IME suggest,
     18   // etc.) is open.
     19   bool IsCandidatePopupOpen() const { return is_candidate_popup_open_; }
     20 
     21   // Overridden from TSFEventRouterObserver:
     22   virtual void OnCandidateWindowCountChanged(size_t window_count) OVERRIDE {
     23     is_candidate_popup_open_ = (window_count != 0);
     24   }
     25 
     26  private:
     27   // True if we know for sure that a candidate window is open.
     28   bool is_candidate_popup_open_;
     29 
     30   DISALLOW_COPY_AND_ASSIGN(TSFEventObserver);
     31 };
     32 
     33 InputMethodTSF::InputMethodTSF(internal::InputMethodDelegate* delegate,
     34                                HWND toplevel_window_handle)
     35     : InputMethodWin(delegate, toplevel_window_handle),
     36       tsf_event_observer_(new TSFEventObserver()),
     37       tsf_event_router_(new TSFEventRouter(tsf_event_observer_.get())) {
     38   // In non-Aura environment, appropriate callbacks to OnFocus() and OnBlur()
     39   // are not implemented yet. To work around this limitation, here we use
     40   // "always focused" model.
     41   // TODO(ime): Fix the caller of OnFocus() and OnBlur() so that appropriate
     42   // focus event will be passed.
     43   InputMethodWin::OnFocus();
     44 }
     45 
     46 InputMethodTSF::~InputMethodTSF() {}
     47 
     48 void InputMethodTSF::OnFocus() {
     49   // Do not call baseclass' OnFocus() and discard the event being in
     50   // "always focused" model.  See the comment in the constructor.
     51   // TODO(ime): Implement OnFocus once the callers are fixed.
     52 
     53   tsf_event_router_->SetManager(
     54       ui::TSFBridge::GetInstance()->GetThreadManager());
     55 }
     56 
     57 void InputMethodTSF::OnBlur() {
     58   // Do not call baseclass' OnBlur() and discard the event being in
     59   // "always focused" model.  See the comment in the constructor.
     60   // TODO(ime): Implement OnFocus once the callers are fixed.
     61 
     62   tsf_event_router_->SetManager(NULL);
     63 }
     64 
     65 bool InputMethodTSF::OnUntranslatedIMEMessage(
     66     const base::NativeEvent& event, InputMethod::NativeEventResult* result) {
     67   LRESULT original_result = 0;
     68   BOOL handled = FALSE;
     69   // Even when TSF is enabled, following IMM32/Win32 messages must be handled.
     70   switch (event.message) {
     71     case WM_IME_REQUEST:
     72       // Some TSF-native TIPs (Text Input Processors) such as ATOK and Mozc
     73       // still rely on WM_IME_REQUEST message to implement reverse conversion.
     74       original_result = OnImeRequest(
     75           event.message, event.wParam, event.lParam, &handled);
     76       break;
     77     case WM_CHAR:
     78     case WM_SYSCHAR:
     79       // ui::InputMethod interface is responsible for handling Win32 character
     80       // messages. For instance, we will be here in the following cases.
     81       // - TIP is not activated. (e.g, the current language profile is English)
     82       // - TIP does not handle and WM_KEYDOWN and WM_KEYDOWN is translated into
     83       //   WM_CHAR by TranslateMessage API. (e.g, TIP is turned off)
     84       // - Another application sends WM_CHAR through SendMessage API.
     85       original_result = OnChar(
     86           event.hwnd, event.message, event.wParam, event.lParam, &handled);
     87       break;
     88     case WM_DEADCHAR:
     89     case WM_SYSDEADCHAR:
     90       // See the comment in WM_CHAR/WM_SYSCHAR.
     91       original_result = OnDeadChar(
     92           event.message, event.wParam, event.lParam, &handled);
     93       break;
     94   }
     95   if (result)
     96     *result = original_result;
     97   return !!handled;
     98 }
     99 
    100 void InputMethodTSF::OnTextInputTypeChanged(const TextInputClient* client) {
    101   if (IsTextInputClientFocused(client) && IsWindowFocused(client)) {
    102     ui::TSFBridge::GetInstance()->CancelComposition();
    103     ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(client);
    104   }
    105   InputMethodWin::OnTextInputTypeChanged(client);
    106 }
    107 
    108 void InputMethodTSF::OnCaretBoundsChanged(const TextInputClient* client) {
    109   if (IsTextInputClientFocused(client) && IsWindowFocused(client))
    110     ui::TSFBridge::GetInstance()->OnTextLayoutChanged();
    111 }
    112 
    113 void InputMethodTSF::CancelComposition(const TextInputClient* client) {
    114   if (IsTextInputClientFocused(client) && IsWindowFocused(client))
    115     ui::TSFBridge::GetInstance()->CancelComposition();
    116 }
    117 
    118 void InputMethodTSF::SetFocusedTextInputClient(TextInputClient* client) {
    119   if (IsWindowFocused(client)) {
    120     ui::TSFBridge::GetInstance()->SetFocusedClient(
    121         GetAttachedWindowHandle(client), client);
    122   } else if (!client) {
    123     // SetFocusedTextInputClient(NULL) must be interpreted as
    124     // "Remove the attached client".
    125     ui::TSFBridge::GetInstance()->RemoveFocusedClient(
    126         ui::TSFBridge::GetInstance()->GetFocusedTextInputClient());
    127   }
    128   InputMethodWin::SetFocusedTextInputClient(client);
    129 }
    130 
    131 bool InputMethodTSF::IsCandidatePopupOpen() const {
    132   return tsf_event_observer_->IsCandidatePopupOpen();
    133 }
    134 
    135 void InputMethodTSF::OnWillChangeFocusedClient(TextInputClient* focused_before,
    136                                                TextInputClient* focused) {
    137   if (IsWindowFocused(focused_before)) {
    138     ConfirmCompositionText();
    139     ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before);
    140   }
    141 }
    142 
    143 void InputMethodTSF::OnDidChangeFocusedClient(TextInputClient* focused_before,
    144                                               TextInputClient* focused) {
    145   if (IsWindowFocused(focused)) {
    146     ui::TSFBridge::GetInstance()->SetFocusedClient(
    147         GetAttachedWindowHandle(focused), focused);
    148     // Force to update the input type since client's TextInputStateChanged()
    149     // function might not be called if text input types before the client loses
    150     // focus and after it acquires focus again are the same.
    151     OnTextInputTypeChanged(focused);
    152 
    153     // Force to update caret bounds, in case the client thinks that the caret
    154     // bounds has not changed.
    155     OnCaretBoundsChanged(focused);
    156   }
    157 }
    158 
    159 void InputMethodTSF::ConfirmCompositionText() {
    160   if (!IsTextInputTypeNone())
    161     ui::TSFBridge::GetInstance()->ConfirmComposition();
    162 }
    163 
    164 bool InputMethodTSF::IsWindowFocused(const TextInputClient* client) const {
    165   if (!client)
    166     return false;
    167   HWND attached_window_handle = GetAttachedWindowHandle(client);
    168   return attached_window_handle && GetFocus() == attached_window_handle;
    169 }
    170 
    171 }  // namespace ui
    172