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
11namespace ui {
12
13class 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
33InputMethodTSF::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
46InputMethodTSF::~InputMethodTSF() {}
47
48void 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
57void 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
65bool 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
100void InputMethodTSF::OnTextInputTypeChanged(const TextInputClient* client) {
101  if (!IsTextInputClientFocused(client) || !IsWindowFocused(client))
102    return;
103  ui::TSFBridge::GetInstance()->CancelComposition();
104  ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(client);
105}
106
107void InputMethodTSF::OnCaretBoundsChanged(const TextInputClient* client) {
108  if (!IsTextInputClientFocused(client) || !IsWindowFocused(client))
109    return;
110  ui::TSFBridge::GetInstance()->OnTextLayoutChanged();
111}
112
113void InputMethodTSF::CancelComposition(const TextInputClient* client) {
114  if (IsTextInputClientFocused(client) && IsWindowFocused(client))
115    ui::TSFBridge::GetInstance()->CancelComposition();
116}
117
118void InputMethodTSF::DetachTextInputClient(TextInputClient* client) {
119  InputMethodWin::DetachTextInputClient(client);
120  ui::TSFBridge::GetInstance()->RemoveFocusedClient(client);
121}
122
123bool InputMethodTSF::IsCandidatePopupOpen() const {
124  return tsf_event_observer_->IsCandidatePopupOpen();
125}
126
127void InputMethodTSF::OnWillChangeFocusedClient(TextInputClient* focused_before,
128                                               TextInputClient* focused) {
129  if (IsWindowFocused(focused_before)) {
130    ConfirmCompositionText();
131    ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before);
132  }
133}
134
135void InputMethodTSF::OnDidChangeFocusedClient(TextInputClient* focused_before,
136                                              TextInputClient* focused) {
137  if (IsWindowFocused(focused) && IsTextInputClientFocused(focused)) {
138    ui::TSFBridge::GetInstance()->SetFocusedClient(
139        GetAttachedWindowHandle(focused), focused);
140
141    // Force to update the input type since client's TextInputStateChanged()
142    // function might not be called if text input types before the client loses
143    // focus and after it acquires focus again are the same.
144    OnTextInputTypeChanged(focused);
145
146    // Force to update caret bounds, in case the client thinks that the caret
147    // bounds has not changed.
148    OnCaretBoundsChanged(focused);
149  }
150  InputMethodWin::OnDidChangeFocusedClient(focused_before, focused);
151}
152
153void InputMethodTSF::ConfirmCompositionText() {
154  if (!IsTextInputTypeNone())
155    ui::TSFBridge::GetInstance()->ConfirmComposition();
156}
157
158}  // namespace ui
159