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 |
| 17 class TsfEventRouterImpl : public TsfEventRouter, | 18 // TsfEventRouter::TsfEventRouterDelegate ------------------------------------ |
| 18 public ITfUIElementSink, | 19 // The implementation class of ITfUIElementSink, whose member functions will be |
| 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) {} | |
| 26 | 33 |
| 27 virtual ~TsfEventRouterImpl() {} | 34 DECLARE_NOT_AGGREGATABLE(TsfEventRouterDelegate); |
| 28 | 35 |
| 29 // ITfTextEditSink override. | 36 DECLARE_PROTECT_FINAL_CONSTRUCT(); |
| 30 virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE { | |
| 31 return InterlockedIncrement(&ref_count_); | |
| 32 } | |
| 33 | 37 |
| 34 // ITfTextEditSink override. | 38 TsfEventRouterDelegate(); |
| 35 virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE { | 39 ~TsfEventRouterDelegate(); |
| 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 | 40 |
| 44 // ITfTextEditSink override. | 41 HRESULT FinalConstruct(); |
| 45 virtual STDMETHODIMP QueryInterface(REFIID iid, void** result) OVERRIDE { | 42 void FinalRelease(); |
| 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 | 43 |
| 60 // ITfTextEditSink override. | 44 // ITfTextEditSink: |
| 61 virtual STDMETHODIMP OnEndEdit(ITfContext* context, | 45 STDMETHOD_(HRESULT, OnEndEdit)(ITfContext* context, |
| 62 TfEditCookie read_only_cookie, | 46 TfEditCookie read_only_cookie, |
| 63 ITfEditRecord* edit_record) OVERRIDE { | 47 ITfEditRecord* edit_record) OVERRIDE; |
| 64 if (!edit_record || !context) | |
| 65 return E_INVALIDARG; | |
| 66 if (!observer_) | |
| 67 return S_OK; | |
| 68 | 48 |
| 69 // |edit_record| can be used to obtain updated ranges in terms of text | 49 // ITfUiElementSink: |
| 70 // contents and/or text attributes. Here we are interested only in text | 50 STDMETHOD_(HRESULT, BeginUIElement)(DWORD element_id, BOOL* is_show) OVERRIDE; |
| 71 // update so we use TF_GTP_INCL_TEXT and check if there is any range which | 51 STDMETHOD_(HRESULT, UpdateUIElement)(DWORD element_id) OVERRIDE; |
| 72 // contains updated text. | 52 STDMETHOD_(HRESULT, EndUIElement)(DWORD element_id) OVERRIDE; |
| 73 base::win::ScopedComPtr<IEnumTfRanges> ranges; | |
| 74 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, | |
| 75 NULL, | |
| 76 0, | |
| 77 ranges.Receive()))) { | |
| 78 return S_OK; // don't care about failures. | |
| 79 } | |
| 80 | 53 |
| 81 ULONG fetched_count = 0; | 54 // Sets |manager| to be monitored. |manager| can be NULL. |
| 82 base::win::ScopedComPtr<ITfRange> range; | 55 void SetManager(ITfThreadMgr* manager); |
| 83 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count))) | |
| 84 return S_OK; // don't care about failures. | |
| 85 | 56 |
| 86 // |fetched_count| != 0 means there is at least one range that contains | 57 // Returns true if the IME is composing text. |
| 87 // updated texts. | 58 bool IsImeComposing(); |
| 88 if (fetched_count != 0) | |
| 89 observer_->OnTextUpdated(); | |
| 90 return S_OK; | |
| 91 } | |
| 92 | 59 |
| 93 // ITfUiElementSink override. | 60 // Sets |router| to be forward TSF related events. |
| 94 virtual STDMETHODIMP BeginUIElement(DWORD element_id, BOOL* is_show) { | 61 void SetRouter(TsfEventRouter* router); |
| 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 | 62 |
| 151 private: | 63 private: |
| 152 // Returns true if the given |context| is in composing. | 64 // Returns true if the given |context| is composing. |
| 153 static bool IsImeComposingInternal(ITfContext* context) { | 65 static bool IsImeComposingInternal(ITfContext* context); |
| 154 DCHECK(base::win::IsTsfAwareRequired()) | |
| 155 << "Do not call without TSF environment."; | |
| 156 DCHECK(context); | |
| 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 | 66 |
| 169 // Returns true if the given |element_id| represents candidate window. | 67 // Returns true if the given |element_id| represents the candidate window. |
| 170 bool IsCandidateWindowInternal(DWORD element_id) { | 68 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 | 69 |
| 182 // Associates this class with specified |manager|. | 70 // Associates this class with the specified |manager|. |
| 183 void Associate(ITfThreadMgr* thread_manager, Observer* observer) { | 71 void Associate(ITfThreadMgr* thread_manager); |
| 184 DCHECK(base::win::IsTsfAwareRequired()) | |
| 185 << "Do not call without TSF environment."; | |
| 186 DCHECK(thread_manager); | |
| 187 | 72 |
| 188 base::win::ScopedComPtr<ITfDocumentMgr> document_manager; | 73 // Resets the association. This function is safe to call even if there is no |
| 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. | 74 // associated thread manager. |
| 212 void EnsureDeassociated() { | 75 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 | 76 |
| 236 // A context associated with this class. | 77 // A context associated with this class. |
| 237 base::win::ScopedComPtr<ITfContext> context_; | 78 base::win::ScopedComPtr<ITfContext> context_; |
| 238 | 79 |
| 239 // The ITfSource associated with |context_|. | 80 // The ITfSource associated with |context_|. |
| 240 base::win::ScopedComPtr<ITfSource> context_source_; | 81 base::win::ScopedComPtr<ITfSource> context_source_; |
| 241 | 82 |
| 242 // The cookie for |context_source_|. | 83 // The cookie for |context_source_|. |
| 243 DWORD context_source_cookie_; | 84 DWORD context_source_cookie_; |
| 244 | 85 |
| 245 // A UiElementMgr associated with this class. | 86 // A UIElementMgr associated with this class. |
| 246 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_; | 87 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_; |
| 247 | 88 |
| 248 // The ITfSouce associated with |ui_element_manager_|. | 89 // The ITfSouce associated with |ui_element_manager_|. |
| 249 base::win::ScopedComPtr<ITfSource> ui_source_; | 90 base::win::ScopedComPtr<ITfSource> ui_source_; |
| 250 | 91 |
| 92 // The set of currently opened candidate window ids. | |
| 93 std::set<DWORD> open_candidate_window_ids_; | |
| 94 | |
| 251 // The cookie for |ui_source_|. | 95 // The cookie for |ui_source_|. |
| 252 DWORD ui_source_cookie_; | 96 DWORD ui_source_cookie_; |
| 253 | 97 |
| 254 // The set of currently opend candidate window ids. | 98 TsfEventRouter* router_; |
| 255 std::set<DWORD> open_candidate_window_ids_; | 99 |
| 256 | 100 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 }; | 101 }; |
| 262 | 102 |
| 263 /////////////////////////////////////////////////////////////////////////////// | 103 TsfEventRouter::TsfEventRouterDelegate::TsfEventRouterDelegate() |
| 264 // TsfEventRouter | 104 : context_source_cookie_(TF_INVALID_COOKIE), |
| 265 | 105 ui_source_cookie_(TF_INVALID_COOKIE), |
| 266 TsfEventRouter::TsfEventRouter() { | 106 router_(NULL) { |
| 107 } | |
| 108 | |
| 109 TsfEventRouter::TsfEventRouterDelegate::~TsfEventRouterDelegate() {} | |
| 110 | |
| 111 HRESULT TsfEventRouter::TsfEventRouterDelegate::FinalConstruct() { | |
| 112 DCHECK(base::win::IsTsfAwareRequired()) | |
| 113 << "Do not use TsfEventRouter without TSF environment."; | |
| 114 return S_OK; | |
|
cpu_(ooo_6.6-7.5)
2012/10/25 19:30:42
if (!base::win::IsTsfAwareRequired())
return som
Seigo Nonaka
2012/10/25 23:00:58
Sure, move DCHECK TsfEventRoter::TsfEventRouter()
| |
| 115 } | |
| 116 | |
| 117 void TsfEventRouter::TsfEventRouterDelegate::FinalRelease() {} | |
| 118 | |
| 119 void TsfEventRouter::TsfEventRouterDelegate::SetRouter(TsfEventRouter* router) { | |
| 120 router_ = router; | |
| 121 } | |
| 122 | |
| 123 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::OnEndEdit( | |
| 124 ITfContext* context, | |
| 125 TfEditCookie read_only_cookie, | |
| 126 ITfEditRecord* edit_record) { | |
| 127 if (!edit_record || !context) | |
| 128 return E_INVALIDARG; | |
| 129 if (!router_) | |
| 130 return S_OK; | |
| 131 | |
| 132 // |edit_record| can be used to obtain updated ranges in terms of text | |
| 133 // contents and/or text attributes. Here we are interested only in text update | |
| 134 // so we use TF_GTP_INCL_TEXT and check if there is any range which contains | |
| 135 // updated text. | |
| 136 base::win::ScopedComPtr<IEnumTfRanges> ranges; | |
| 137 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0, | |
| 138 ranges.Receive()))) | |
| 139 return S_OK; // Don't care about failures. | |
| 140 | |
| 141 ULONG fetched_count = 0; | |
| 142 base::win::ScopedComPtr<ITfRange> range; | |
| 143 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count))) | |
| 144 return S_OK; // Don't care about failures. | |
| 145 | |
| 146 // |fetched_count| != 0 means there is at least one range that contains | |
| 147 // updated text. | |
| 148 if (fetched_count != 0) | |
| 149 router_->OnTextUpdated(); | |
| 150 return S_OK; | |
| 151 } | |
| 152 | |
| 153 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::BeginUIElement( | |
| 154 DWORD element_id, | |
| 155 BOOL* is_show) { | |
| 156 if (is_show) | |
| 157 *is_show = TRUE; // Without this the UI element will not be shown. | |
| 158 | |
| 159 if (!IsCandidateWindowInternal(element_id)) | |
| 160 return S_OK; | |
| 161 | |
| 162 std::pair<std::set<DWORD>::iterator, bool> insert_result = | |
| 163 open_candidate_window_ids_.insert(element_id); | |
| 164 // Don't call if |router_| is null or |element_id| is already handled. | |
| 165 if (router_ && insert_result.second) | |
| 166 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 167 | |
| 168 return S_OK; | |
| 169 } | |
| 170 | |
| 171 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::UpdateUIElement( | |
| 172 DWORD element_id) { | |
| 173 return S_OK; | |
| 174 } | |
| 175 | |
| 176 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::EndUIElement( | |
| 177 DWORD element_id) { | |
| 178 if ((open_candidate_window_ids_.erase(element_id) != 0) && router_) | |
| 179 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); | |
| 180 return S_OK; | |
| 181 } | |
| 182 | |
| 183 void TsfEventRouter::TsfEventRouterDelegate::SetManager(ITfThreadMgr* manager) { | |
| 184 EnsureDeassociated(); | |
| 185 if (manager) | |
| 186 Associate(manager); | |
| 187 } | |
| 188 | |
| 189 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposing() { | |
| 190 return context_ && IsImeComposingInternal(context_); | |
| 191 } | |
| 192 | |
| 193 // static | |
| 194 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposingInternal( | |
| 195 ITfContext* context) { | |
| 196 DCHECK(context); | |
| 197 base::win::ScopedComPtr<ITfContextComposition> context_composition; | |
| 198 if (FAILED(context_composition.QueryFrom(context))) | |
| 199 return false; | |
| 200 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view; | |
| 201 if (FAILED(context_composition->EnumCompositions( | |
| 202 enum_composition_view.Receive()))) | |
| 203 return false; | |
| 204 base::win::ScopedComPtr<ITfCompositionView> composition_view; | |
| 205 return enum_composition_view->Next(1, composition_view.Receive(), | |
| 206 NULL) == S_OK; | |
| 207 } | |
| 208 | |
| 209 bool TsfEventRouter::TsfEventRouterDelegate::IsCandidateWindowInternal( | |
| 210 DWORD element_id) { | |
| 211 DCHECK(ui_element_manager_.get()); | |
| 212 base::win::ScopedComPtr<ITfUIElement> ui_element; | |
| 213 if (FAILED(ui_element_manager_->GetUIElement(element_id, | |
| 214 ui_element.Receive()))) | |
| 215 return false; | |
| 216 base::win::ScopedComPtr<ITfCandidateListUIElement> candidate_list_ui_element; | |
| 217 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element)); | |
| 218 } | |
| 219 | |
| 220 void TsfEventRouter::TsfEventRouterDelegate::Associate( | |
| 221 ITfThreadMgr* thread_manager) { | |
| 222 DCHECK(thread_manager); | |
| 223 base::win::ScopedComPtr<ITfDocumentMgr> document_manager; | |
| 224 if (FAILED(thread_manager->GetFocus(document_manager.Receive())) || | |
| 225 FAILED(document_manager->GetBase(context_.Receive())) || | |
| 226 FAILED(context_source_.QueryFrom(context_))) | |
| 227 return; | |
| 228 context_source_->AdviseSink(IID_ITfTextEditSink, | |
| 229 static_cast<ITfTextEditSink*>(this), | |
| 230 &context_source_cookie_); | |
| 231 | |
| 232 if (FAILED(ui_element_manager_.QueryFrom(thread_manager)) || | |
| 233 FAILED(ui_source_.QueryFrom(ui_element_manager_))) | |
| 234 return; | |
| 235 ui_source_->AdviseSink(IID_ITfUIElementSink, | |
| 236 static_cast<ITfUIElementSink*>(this), | |
| 237 &ui_source_cookie_); | |
| 238 } | |
| 239 | |
| 240 void TsfEventRouter::TsfEventRouterDelegate::EnsureDeassociated() { | |
| 241 context_.Release(); | |
| 242 | |
| 243 if (context_source_) { | |
| 244 context_source_->UnadviseSink(context_source_cookie_); | |
| 245 context_source_.Release(); | |
| 246 } | |
| 247 context_source_cookie_ = TF_INVALID_COOKIE; | |
| 248 | |
| 249 ui_element_manager_.Release(); | |
| 250 if (ui_source_) { | |
| 251 ui_source_->UnadviseSink(ui_source_cookie_); | |
| 252 ui_source_.Release(); | |
| 253 } | |
| 254 ui_source_cookie_ = TF_INVALID_COOKIE; | |
| 255 } | |
| 256 | |
| 257 // TsfEventRouter ------------------------------------------------------------ | |
| 258 TsfEventRouter::TsfEventRouter(TsfEventRouterObserver* observer) | |
| 259 : observer_(observer) { | |
|
cpu_(ooo_6.6-7.5)
2012/10/25 19:30:42
, delegate_(NULL)
Seigo Nonaka
2012/10/25 23:00:58
Done.
| |
| 260 ui::win::CreateATLModuleIfNeeded(); | |
| 261 if (SUCCEEDED(CComObject<TsfEventRouterDelegate>::CreateInstance( | |
| 262 &delegate_))) { | |
| 263 delegate_->AddRef(); | |
| 264 delegate_->SetRouter(this); | |
| 265 } | |
| 267 } | 266 } |
| 268 | 267 |
| 269 TsfEventRouter::~TsfEventRouter() { | 268 TsfEventRouter::~TsfEventRouter() { |
| 270 } | 269 if (delegate_) { |
| 271 | 270 delegate_->SetManager(NULL); |
| 272 TsfEventRouter* TsfEventRouter::Create() { | 271 delegate_->SetRouter(NULL); |
| 273 return new TsfEventRouterImpl(); | 272 delegate_->Release(); |
| 273 delegate_ = NULL; | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 void TsfEventRouter::SetManager(ITfThreadMgr* manager) { | |
| 278 delegate_->SetManager(manager); | |
| 279 } | |
| 280 | |
| 281 bool TsfEventRouter::IsImeComposing() { | |
| 282 return delegate_->IsImeComposing(); | |
| 283 } | |
| 284 | |
| 285 void TsfEventRouter::OnTextUpdated() { | |
| 286 observer_->OnTextUpdated(); | |
| 287 } | |
| 288 | |
| 289 void TsfEventRouter::OnCandidateWindowCountChanged(size_t window_count) { | |
| 290 observer_->OnCandidateWindowCountChanged(window_count); | |
| 274 } | 291 } |
| 275 | 292 |
| 276 } // namespace ui | 293 } // namespace ui |
| OLD | NEW |