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