普通文本  |  159行  |  5.85 KB

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/ime/input_method_tsf.h"

#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/win/tsf_bridge.h"
#include "ui/base/ime/win/tsf_event_router.h"

namespace ui {

class InputMethodTSF::TSFEventObserver : public TSFEventRouterObserver {
 public:
  TSFEventObserver() : is_candidate_popup_open_(false) {}

  // Returns true if we know for sure that a candidate window (or IME suggest,
  // etc.) is open.
  bool IsCandidatePopupOpen() const { return is_candidate_popup_open_; }

  // Overridden from TSFEventRouterObserver:
  virtual void OnCandidateWindowCountChanged(size_t window_count) OVERRIDE {
    is_candidate_popup_open_ = (window_count != 0);
  }

 private:
  // True if we know for sure that a candidate window is open.
  bool is_candidate_popup_open_;

  DISALLOW_COPY_AND_ASSIGN(TSFEventObserver);
};

InputMethodTSF::InputMethodTSF(internal::InputMethodDelegate* delegate,
                               HWND toplevel_window_handle)
    : InputMethodWin(delegate, toplevel_window_handle),
      tsf_event_observer_(new TSFEventObserver()),
      tsf_event_router_(new TSFEventRouter(tsf_event_observer_.get())) {
  // In non-Aura environment, appropriate callbacks to OnFocus() and OnBlur()
  // are not implemented yet. To work around this limitation, here we use
  // "always focused" model.
  // TODO(ime): Fix the caller of OnFocus() and OnBlur() so that appropriate
  // focus event will be passed.
  InputMethodWin::OnFocus();
}

InputMethodTSF::~InputMethodTSF() {}

void InputMethodTSF::OnFocus() {
  // Do not call baseclass' OnFocus() and discard the event being in
  // "always focused" model.  See the comment in the constructor.
  // TODO(ime): Implement OnFocus once the callers are fixed.

  tsf_event_router_->SetManager(
      ui::TSFBridge::GetInstance()->GetThreadManager());
}

void InputMethodTSF::OnBlur() {
  // Do not call baseclass' OnBlur() and discard the event being in
  // "always focused" model.  See the comment in the constructor.
  // TODO(ime): Implement OnFocus once the callers are fixed.

  tsf_event_router_->SetManager(NULL);
}

bool InputMethodTSF::OnUntranslatedIMEMessage(
    const base::NativeEvent& event, InputMethod::NativeEventResult* result) {
  LRESULT original_result = 0;
  BOOL handled = FALSE;
  // Even when TSF is enabled, following IMM32/Win32 messages must be handled.
  switch (event.message) {
    case WM_IME_REQUEST:
      // Some TSF-native TIPs (Text Input Processors) such as ATOK and Mozc
      // still rely on WM_IME_REQUEST message to implement reverse conversion.
      original_result = OnImeRequest(
          event.message, event.wParam, event.lParam, &handled);
      break;
    case WM_CHAR:
    case WM_SYSCHAR:
      // ui::InputMethod interface is responsible for handling Win32 character
      // messages. For instance, we will be here in the following cases.
      // - TIP is not activated. (e.g, the current language profile is English)
      // - TIP does not handle and WM_KEYDOWN and WM_KEYDOWN is translated into
      //   WM_CHAR by TranslateMessage API. (e.g, TIP is turned off)
      // - Another application sends WM_CHAR through SendMessage API.
      original_result = OnChar(
          event.hwnd, event.message, event.wParam, event.lParam, &handled);
      break;
    case WM_DEADCHAR:
    case WM_SYSDEADCHAR:
      // See the comment in WM_CHAR/WM_SYSCHAR.
      original_result = OnDeadChar(
          event.message, event.wParam, event.lParam, &handled);
      break;
  }
  if (result)
    *result = original_result;
  return !!handled;
}

void InputMethodTSF::OnTextInputTypeChanged(const TextInputClient* client) {
  if (!IsTextInputClientFocused(client) || !IsWindowFocused(client))
    return;
  ui::TSFBridge::GetInstance()->CancelComposition();
  ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(client);
}

void InputMethodTSF::OnCaretBoundsChanged(const TextInputClient* client) {
  if (!IsTextInputClientFocused(client) || !IsWindowFocused(client))
    return;
  ui::TSFBridge::GetInstance()->OnTextLayoutChanged();
}

void InputMethodTSF::CancelComposition(const TextInputClient* client) {
  if (IsTextInputClientFocused(client) && IsWindowFocused(client))
    ui::TSFBridge::GetInstance()->CancelComposition();
}

void InputMethodTSF::DetachTextInputClient(TextInputClient* client) {
  InputMethodWin::DetachTextInputClient(client);
  ui::TSFBridge::GetInstance()->RemoveFocusedClient(client);
}

bool InputMethodTSF::IsCandidatePopupOpen() const {
  return tsf_event_observer_->IsCandidatePopupOpen();
}

void InputMethodTSF::OnWillChangeFocusedClient(TextInputClient* focused_before,
                                               TextInputClient* focused) {
  if (IsWindowFocused(focused_before)) {
    ConfirmCompositionText();
    ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before);
  }
}

void InputMethodTSF::OnDidChangeFocusedClient(TextInputClient* focused_before,
                                              TextInputClient* focused) {
  if (IsWindowFocused(focused) && IsTextInputClientFocused(focused)) {
    ui::TSFBridge::GetInstance()->SetFocusedClient(
        GetAttachedWindowHandle(focused), focused);

    // Force to update the input type since client's TextInputStateChanged()
    // function might not be called if text input types before the client loses
    // focus and after it acquires focus again are the same.
    OnTextInputTypeChanged(focused);

    // Force to update caret bounds, in case the client thinks that the caret
    // bounds has not changed.
    OnCaretBoundsChanged(focused);
  }
  InputMethodWin::OnDidChangeFocusedClient(focused_before, focused);
}

void InputMethodTSF::ConfirmCompositionText() {
  if (!IsTextInputTypeNone())
    ui::TSFBridge::GetInstance()->ConfirmComposition();
}

}  // namespace ui