Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/ime/win/tsf_event_router.h" | 5 #include "ui/base/ime/win/tsf_event_router.h" |
| 6 | 6 |
| 7 #include <msctf.h> | 7 #include <msctf.h> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/win/scoped_comptr.h" | 12 #include "base/win/scoped_comptr.h" |
| 13 #include "base/win/metro.h" | 13 #include "base/win/metro.h" |
| 14 #include "ui/base/win/atl_module.h" | |
| 14 | 15 |
| 15 namespace ui { | 16 namespace ui { |
| 16 | 17 |
|
Peter Kasting
2012/10/26 02:31:57
Tiny nit: I suggest one more blank line above each
Seigo Nonaka
2012/10/26 02:40:27
Done.
| |
| 17 class TsfEventRouterImpl : public TsfEventRouter, | 18 // TsfEventRouter::TsfEventRouterDelegate ------------------------------------ |
| 18 public ITfUIElementSink, | 19 // The implementation class of ITfUIElementSink, whose member functions will be |
|
Peter Kasting
2012/10/26 02:31:57
Nit: Add blank line here
Seigo Nonaka
2012/10/26 02:40:27
Done.
| |
| 19 public ITfTextEditSink { | 20 // called back by TSF when the UI element status is changed, for example when |
| 21 // the candidate window is opened or closed. This class also implements | |
| 22 // ITfTextEditSink, whose member function is called back by TSF when the text | |
| 23 // editting session is finished. | |
| 24 class ATL_NO_VTABLE TsfEventRouter::TsfEventRouterDelegate | |
| 25 : public ATL::CComObjectRootEx<CComSingleThreadModel>, | |
| 26 public ITfUIElementSink, | |
| 27 public ITfTextEditSink { | |
| 20 public: | 28 public: |
| 21 TsfEventRouterImpl() | 29 BEGIN_COM_MAP(TsfEventRouterDelegate) |
| 22 : observer_(NULL), | 30 COM_INTERFACE_ENTRY(ITfUIElementSink) |
| 23 context_source_cookie_(TF_INVALID_COOKIE), | 31 COM_INTERFACE_ENTRY(ITfTextEditSink) |
| 24 ui_source_cookie_(TF_INVALID_COOKIE), | 32 END_COM_MAP() |
| 25 ref_count_(0) {} | 33 |
| 26 | 34 TsfEventRouterDelegate(); |
| 27 virtual ~TsfEventRouterImpl() {} | 35 ~TsfEventRouterDelegate(); |
| 28 | 36 |
| 29 // ITfTextEditSink override. | 37 // ITfTextEditSink: |
| 30 virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE { | 38 STDMETHOD_(HRESULT, OnEndEdit)(ITfContext* context, |
| 31 return InterlockedIncrement(&ref_count_); | |
| 32 } | |
| 33 | |
| 34 // ITfTextEditSink override. | |
| 35 virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE { | |
| 36 const LONG count = InterlockedDecrement(&ref_count_); | |
| 37 if (!count) { | |
| 38 delete this; | |
| 39 return 0; | |
| 40 } | |
| 41 return static_cast<ULONG>(count); | |
| 42 } | |
| 43 | |
| 44 // ITfTextEditSink override. | |
| 45 virtual STDMETHODIMP QueryInterface(REFIID iid, void** result) OVERRIDE { | |
| 46 if (!result) | |
| 47 return E_INVALIDARG; | |
| 48 if (iid == IID_IUnknown || iid == IID_ITfTextEditSink) { | |
| 49 *result = static_cast<ITfTextEditSink*>(this); | |
| 50 } else if (iid == IID_ITfUIElementSink) { | |
| 51 *result = static_cast<ITfUIElementSink*>(this); | |
| 52 } else { | |
| 53 *result = NULL; | |
| 54 return E_NOINTERFACE; | |
| 55 } | |
| 56 AddRef(); | |
| 57 return S_OK; | |
| 58 } | |
| 59 | |
| 60 // ITfTextEditSink override. | |
| 61 virtual STDMETHODIMP OnEndEdit(ITfContext* context, | |
| 62 TfEditCookie read_only_cookie, | 39 TfEditCookie read_only_cookie, |
| 63 ITfEditRecord* edit_record) OVERRIDE { | 40 ITfEditRecord* edit_record) OVERRIDE; |
| 64 if (!edit_record || !context) | 41 |
| 65 return E_INVALIDARG; | 42 // ITfUiElementSink: |
| 66 if (!observer_) | 43 STDMETHOD_(HRESULT, BeginUIElement)(DWORD element_id, BOOL* is_show) OVERRIDE; |
| 67 return S_OK; | 44 STDMETHOD_(HRESULT, UpdateUIElement)(DWORD element_id) OVERRIDE; |
| 68 | 45 STDMETHOD_(HRESULT, EndUIElement)(DWORD element_id) OVERRIDE; |
| 69 // |edit_record| can be used to obtain updated ranges in terms of text | 46 |
| 70 // contents and/or text attributes. Here we are interested only in text | 47 // Sets |thread_manager| to be monitored. |thread_manager| can be NULL. |
| 71 // update so we use TF_GTP_INCL_TEXT and check if there is any range which | 48 void SetManager(ITfThreadMgr* thread_manager); |
|
Peter Kasting
2012/10/26 02:31:57
Nit: I suggest using the same parameter name in th
Seigo Nonaka
2012/10/26 02:40:27
Done.
| |
| 72 // contains updated text. | 49 |
| 73 base::win::ScopedComPtr<IEnumTfRanges> ranges; | 50 // Returns true if the IME is composing text. |
| 74 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, | 51 bool IsImeComposing(); |
| 75 NULL, | 52 |
| 76 0, | 53 // Sets |router| to be forwarded TSF-related events. |
| 77 ranges.Receive()))) { | 54 void SetRouter(TsfEventRouter* router); |
| 78 return S_OK; // don't care about failures. | |
| 79 } | |
| 80 | |
| 81 ULONG fetched_count = 0; | |
| 82 base::win::ScopedComPtr<ITfRange> range; | |
| 83 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count))) | |
| 84 return S_OK; // don't care about failures. | |
| 85 | |
| 86 // |fetched_count| != 0 means there is at least one range that contains | |
| 87 // updated texts. | |
| 88 if (fetched_count != 0) | |
| 89 observer_->OnTextUpdated(); | |
| 90 return S_OK; | |
| 91 } | |
| 92 | |
| 93 // ITfUiElementSink override. | |
| 94 virtual STDMETHODIMP BeginUIElement(DWORD element_id, BOOL* is_show) { | |
| 95 if (is_show) | |
| 96 *is_show = TRUE; // without this the UI element will not be shown. | |
| 97 | |
| 98 if (!IsCandidateWindowInternal(element_id)) | |
| 99 return S_OK; | |
| 100 | |
| 101 std::pair<std::set<DWORD>::iterator, bool> insert_result = | |
| 102 open_candidate_window_ids_.insert(element_id); | |
| 103 | |
| 104 if (!observer_) | |
| 105 return S_OK; | |
| 106 | |
| 107 // Don't call if |element_id| is already handled. | |
| 108 if (!insert_result.second) | |
| 109 return S_OK; | |
| 110 | |
| 111 observer_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 112 | |
| 113 return S_OK; | |
| 114 } | |
| 115 | |
| 116 // ITfUiElementSink override. | |
| 117 virtual STDMETHODIMP UpdateUIElement(DWORD element_id) { | |
| 118 return S_OK; | |
| 119 } | |
| 120 | |
| 121 // ITfUiElementSink override. | |
| 122 virtual STDMETHODIMP EndUIElement(DWORD element_id) { | |
| 123 if (open_candidate_window_ids_.erase(element_id) == 0) | |
| 124 return S_OK; | |
| 125 | |
| 126 if (!observer_) | |
| 127 return S_OK; | |
| 128 | |
| 129 observer_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 130 | |
| 131 return S_OK; | |
| 132 } | |
| 133 | |
| 134 // TsfEventRouter override. | |
| 135 virtual void SetManager(ITfThreadMgr* manager, Observer* observer) OVERRIDE { | |
| 136 EnsureDeassociated(); | |
| 137 if (manager && observer) { | |
| 138 Associate(manager, observer); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 // TsfEventRouter override. | |
| 143 virtual bool IsImeComposing() OVERRIDE { | |
| 144 DCHECK(base::win::IsTsfAwareRequired()) | |
| 145 << "Do not call without TSF environment."; | |
| 146 if (!context_) | |
| 147 return false; | |
| 148 return IsImeComposingInternal(context_); | |
| 149 } | |
| 150 | 55 |
| 151 private: | 56 private: |
| 152 // Returns true if the given |context| is in composing. | 57 // Returns true if the given |context| is composing. |
| 153 static bool IsImeComposingInternal(ITfContext* context) { | 58 static bool IsImeComposingInternal(ITfContext* context); |
| 154 DCHECK(base::win::IsTsfAwareRequired()) | 59 |
| 155 << "Do not call without TSF environment."; | 60 // Returns true if the given |element_id| represents the candidate window. |
| 156 DCHECK(context); | 61 bool IsCandidateWindowInternal(DWORD element_id); |
| 157 base::win::ScopedComPtr<ITfContextComposition> context_composition; | |
| 158 if (FAILED(context_composition.QueryFrom(context))) | |
| 159 return false; | |
| 160 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view; | |
| 161 if (FAILED(context_composition->EnumCompositions( | |
| 162 enum_composition_view.Receive()))) | |
| 163 return false; | |
| 164 base::win::ScopedComPtr<ITfCompositionView> composition_view; | |
| 165 return enum_composition_view->Next(1, composition_view.Receive(), | |
| 166 NULL) == S_OK; | |
| 167 } | |
| 168 | |
| 169 // Returns true if the given |element_id| represents candidate window. | |
| 170 bool IsCandidateWindowInternal(DWORD element_id) { | |
| 171 DCHECK(ui_element_manager_.get()); | |
| 172 base::win::ScopedComPtr<ITfUIElement> ui_element; | |
| 173 if (FAILED(ui_element_manager_->GetUIElement(element_id, | |
| 174 ui_element.Receive()))) { | |
| 175 return false; | |
| 176 } | |
| 177 base::win::ScopedComPtr<ITfCandidateListUIElement> | |
| 178 candidate_list_ui_element; | |
| 179 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element)); | |
| 180 } | |
| 181 | |
| 182 // Associates this class with specified |manager|. | |
| 183 void Associate(ITfThreadMgr* thread_manager, Observer* observer) { | |
| 184 DCHECK(base::win::IsTsfAwareRequired()) | |
| 185 << "Do not call without TSF environment."; | |
| 186 DCHECK(thread_manager); | |
| 187 | |
| 188 base::win::ScopedComPtr<ITfDocumentMgr> document_manager; | |
| 189 if (FAILED(thread_manager->GetFocus(document_manager.Receive()))) | |
| 190 return; | |
| 191 | |
| 192 if (FAILED(document_manager->GetBase(context_.Receive()))) | |
| 193 return; | |
| 194 if (FAILED(context_source_.QueryFrom(context_))) | |
| 195 return; | |
| 196 context_source_->AdviseSink(IID_ITfTextEditSink, | |
| 197 static_cast<ITfTextEditSink*>(this), | |
| 198 &context_source_cookie_); | |
| 199 | |
| 200 if (FAILED(ui_element_manager_.QueryFrom(thread_manager))) | |
| 201 return; | |
| 202 if (FAILED(ui_source_.QueryFrom(ui_element_manager_))) | |
| 203 return; | |
| 204 ui_source_->AdviseSink(IID_ITfUIElementSink, | |
| 205 static_cast<ITfUIElementSink*>(this), | |
| 206 &ui_source_cookie_); | |
| 207 observer_ = observer; | |
| 208 } | |
| 209 | |
| 210 // Resets the association, this function is safe to call if there is no | |
| 211 // associated thread manager. | |
| 212 void EnsureDeassociated() { | |
| 213 DCHECK(base::win::IsTsfAwareRequired()) | |
| 214 << "Do not call without TSF environment."; | |
| 215 | |
| 216 context_.Release(); | |
| 217 | |
| 218 if (context_source_) { | |
| 219 context_source_->UnadviseSink(context_source_cookie_); | |
| 220 context_source_.Release(); | |
| 221 } | |
| 222 context_source_cookie_ = TF_INVALID_COOKIE; | |
| 223 | |
| 224 ui_element_manager_.Release(); | |
| 225 if (ui_source_) { | |
| 226 ui_source_->UnadviseSink(ui_source_cookie_); | |
| 227 ui_source_.Release(); | |
| 228 } | |
| 229 ui_source_cookie_ = TF_INVALID_COOKIE; | |
| 230 | |
| 231 observer_ = NULL; | |
| 232 } | |
| 233 | |
| 234 Observer* observer_; | |
| 235 | 62 |
| 236 // A context associated with this class. | 63 // A context associated with this class. |
| 237 base::win::ScopedComPtr<ITfContext> context_; | 64 base::win::ScopedComPtr<ITfContext> context_; |
| 238 | 65 |
| 239 // The ITfSource associated with |context_|. | 66 // The ITfSource associated with |context_|. |
| 240 base::win::ScopedComPtr<ITfSource> context_source_; | 67 base::win::ScopedComPtr<ITfSource> context_source_; |
| 241 | 68 |
| 242 // The cookie for |context_source_|. | 69 // The cookie for |context_source_|. |
| 243 DWORD context_source_cookie_; | 70 DWORD context_source_cookie_; |
| 244 | 71 |
| 245 // A UiElementMgr associated with this class. | 72 // A UIElementMgr associated with this class. |
| 246 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_; | 73 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_; |
| 247 | 74 |
| 248 // The ITfSouce associated with |ui_element_manager_|. | 75 // The ITfSouce associated with |ui_element_manager_|. |
| 249 base::win::ScopedComPtr<ITfSource> ui_source_; | 76 base::win::ScopedComPtr<ITfSource> ui_source_; |
| 250 | 77 |
| 78 // The set of currently opened candidate window ids. | |
| 79 std::set<DWORD> open_candidate_window_ids_; | |
| 80 | |
| 251 // The cookie for |ui_source_|. | 81 // The cookie for |ui_source_|. |
| 252 DWORD ui_source_cookie_; | 82 DWORD ui_source_cookie_; |
| 253 | 83 |
| 254 // The set of currently opend candidate window ids. | 84 TsfEventRouter* router_; |
| 255 std::set<DWORD> open_candidate_window_ids_; | 85 |
| 256 | 86 DISALLOW_COPY_AND_ASSIGN(TsfEventRouterDelegate); |
| 257 // The reference count of this instance. | |
| 258 volatile LONG ref_count_; | |
| 259 | |
| 260 DISALLOW_COPY_AND_ASSIGN(TsfEventRouterImpl); | |
| 261 }; | 87 }; |
| 262 | 88 |
| 263 /////////////////////////////////////////////////////////////////////////////// | 89 TsfEventRouter::TsfEventRouterDelegate::TsfEventRouterDelegate() |
| 264 // TsfEventRouter | 90 : context_source_cookie_(TF_INVALID_COOKIE), |
| 265 | 91 ui_source_cookie_(TF_INVALID_COOKIE), |
| 266 TsfEventRouter::TsfEventRouter() { | 92 router_(NULL) { |
| 93 } | |
| 94 | |
| 95 TsfEventRouter::TsfEventRouterDelegate::~TsfEventRouterDelegate() {} | |
| 96 | |
| 97 void TsfEventRouter::TsfEventRouterDelegate::SetRouter(TsfEventRouter* router) { | |
| 98 router_ = router; | |
| 99 } | |
| 100 | |
| 101 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::OnEndEdit( | |
| 102 ITfContext* context, | |
| 103 TfEditCookie read_only_cookie, | |
| 104 ITfEditRecord* edit_record) { | |
| 105 if (!edit_record || !context) | |
| 106 return E_INVALIDARG; | |
| 107 if (!router_) | |
| 108 return S_OK; | |
| 109 | |
| 110 // |edit_record| can be used to obtain updated ranges in terms of text | |
| 111 // contents and/or text attributes. Here we are interested only in text update | |
| 112 // so we use TF_GTP_INCL_TEXT and check if there is any range which contains | |
| 113 // updated text. | |
| 114 base::win::ScopedComPtr<IEnumTfRanges> ranges; | |
| 115 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, | |
| 116 ranges.Receive()))) | |
| 117 return S_OK; // Don't care about failures. | |
| 118 | |
| 119 ULONG fetched_count = 0; | |
| 120 base::win::ScopedComPtr<ITfRange> range; | |
| 121 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count))) | |
| 122 return S_OK; // Don't care about failures. | |
| 123 | |
| 124 // |fetched_count| != 0 means there is at least one range that contains | |
| 125 // updated text. | |
| 126 if (fetched_count != 0) | |
| 127 router_->OnTextUpdated(); | |
| 128 return S_OK; | |
| 129 } | |
| 130 | |
| 131 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::BeginUIElement( | |
| 132 DWORD element_id, | |
| 133 BOOL* is_show) { | |
| 134 if (is_show) | |
| 135 *is_show = TRUE; // Without this the UI element will not be shown. | |
| 136 | |
| 137 if (!IsCandidateWindowInternal(element_id)) | |
| 138 return S_OK; | |
| 139 | |
| 140 std::pair<std::set<DWORD>::iterator, bool> insert_result = | |
| 141 open_candidate_window_ids_.insert(element_id); | |
| 142 // Don't call if |router_| is null or |element_id| is already handled. | |
| 143 if (router_ && insert_result.second) | |
| 144 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 145 | |
| 146 return S_OK; | |
| 147 } | |
| 148 | |
| 149 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::UpdateUIElement( | |
| 150 DWORD element_id) { | |
| 151 return S_OK; | |
| 152 } | |
| 153 | |
| 154 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::EndUIElement( | |
| 155 DWORD element_id) { | |
| 156 if ((open_candidate_window_ids_.erase(element_id) != 0) && router_) | |
| 157 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 158 return S_OK; | |
| 159 } | |
| 160 | |
| 161 void TsfEventRouter::TsfEventRouterDelegate::SetManager( | |
| 162 ITfThreadMgr* thread_manager) { | |
| 163 context_.Release(); | |
| 164 | |
| 165 if (context_source_) { | |
| 166 context_source_->UnadviseSink(context_source_cookie_); | |
| 167 context_source_.Release(); | |
| 168 } | |
| 169 context_source_cookie_ = TF_INVALID_COOKIE; | |
| 170 | |
| 171 ui_element_manager_.Release(); | |
| 172 if (ui_source_) { | |
| 173 ui_source_->UnadviseSink(ui_source_cookie_); | |
| 174 ui_source_.Release(); | |
| 175 } | |
| 176 ui_source_cookie_ = TF_INVALID_COOKIE; | |
| 177 | |
| 178 if (!thread_manager) | |
| 179 return; | |
| 180 | |
| 181 base::win::ScopedComPtr<ITfDocumentMgr> document_manager; | |
| 182 if (FAILED(thread_manager->GetFocus(document_manager.Receive())) || | |
| 183 FAILED(document_manager->GetBase(context_.Receive())) || | |
| 184 FAILED(context_source_.QueryFrom(context_))) | |
| 185 return; | |
| 186 context_source_->AdviseSink(IID_ITfTextEditSink, | |
| 187 static_cast<ITfTextEditSink*>(this), | |
| 188 &context_source_cookie_); | |
| 189 | |
| 190 if (FAILED(ui_element_manager_.QueryFrom(thread_manager)) || | |
| 191 FAILED(ui_source_.QueryFrom(ui_element_manager_))) | |
| 192 return; | |
| 193 ui_source_->AdviseSink(IID_ITfUIElementSink, | |
| 194 static_cast<ITfUIElementSink*>(this), | |
| 195 &ui_source_cookie_); | |
| 196 } | |
| 197 | |
| 198 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposing() { | |
| 199 return context_ && IsImeComposingInternal(context_); | |
| 200 } | |
| 201 | |
| 202 // static | |
| 203 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposingInternal( | |
| 204 ITfContext* context) { | |
| 205 DCHECK(context); | |
| 206 base::win::ScopedComPtr<ITfContextComposition> context_composition; | |
| 207 if (FAILED(context_composition.QueryFrom(context))) | |
| 208 return false; | |
| 209 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view; | |
| 210 if (FAILED(context_composition->EnumCompositions( | |
| 211 enum_composition_view.Receive()))) | |
| 212 return false; | |
| 213 base::win::ScopedComPtr<ITfCompositionView> composition_view; | |
| 214 return enum_composition_view->Next(1, composition_view.Receive(), | |
| 215 NULL) == S_OK; | |
| 216 } | |
| 217 | |
| 218 bool TsfEventRouter::TsfEventRouterDelegate::IsCandidateWindowInternal( | |
| 219 DWORD element_id) { | |
| 220 DCHECK(ui_element_manager_.get()); | |
| 221 base::win::ScopedComPtr<ITfUIElement> ui_element; | |
| 222 if (FAILED(ui_element_manager_->GetUIElement(element_id, | |
| 223 ui_element.Receive()))) | |
| 224 return false; | |
| 225 base::win::ScopedComPtr<ITfCandidateListUIElement> candidate_list_ui_element; | |
| 226 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element)); | |
| 227 } | |
| 228 | |
| 229 | |
| 230 // TsfEventRouter ------------------------------------------------------------ | |
| 231 | |
| 232 TsfEventRouter::TsfEventRouter(TsfEventRouterObserver* observer) | |
| 233 : observer_(observer), | |
| 234 delegate_(NULL) { | |
| 235 DCHECK(base::win::IsTsfAwareRequired()) | |
| 236 << "Do not use TsfEventRouter without TSF environment."; | |
| 237 CComObject<TsfEventRouterDelegate>* delegate; | |
| 238 ui::win::CreateATLModuleIfNeeded(); | |
| 239 if (SUCCEEDED(CComObject<TsfEventRouterDelegate>::CreateInstance( | |
| 240 &delegate))) { | |
| 241 delegate->AddRef(); | |
| 242 delegate_.Attach(delegate_); | |
| 243 delegate_->SetRouter(this); | |
| 244 } | |
| 267 } | 245 } |
| 268 | 246 |
| 269 TsfEventRouter::~TsfEventRouter() { | 247 TsfEventRouter::~TsfEventRouter() { |
| 270 } | 248 if (delegate_) { |
| 271 | 249 delegate_->SetManager(NULL); |
| 272 TsfEventRouter* TsfEventRouter::Create() { | 250 delegate_->SetRouter(NULL); |
| 273 return new TsfEventRouterImpl(); | 251 } |
| 252 } | |
| 253 | |
| 254 void TsfEventRouter::SetManager(ITfThreadMgr* manager) { | |
| 255 delegate_->SetManager(manager); | |
| 256 } | |
| 257 | |
| 258 bool TsfEventRouter::IsImeComposing() { | |
| 259 return delegate_->IsImeComposing(); | |
| 260 } | |
| 261 | |
| 262 void TsfEventRouter::OnTextUpdated() { | |
| 263 observer_->OnTextUpdated(); | |
| 264 } | |
| 265 | |
| 266 void TsfEventRouter::OnCandidateWindowCountChanged(size_t window_count) { | |
| 267 observer_->OnCandidateWindowCountChanged(window_count); | |
| 274 } | 268 } |
| 275 | 269 |
| 276 } // namespace ui | 270 } // namespace ui |
| OLD | NEW |