Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: ui/base/ime/win/tsf_event_router.cc

Issue 11235023: Redesign: Remove TsfEventRouter interface. (Closed) Base URL: http://git.chromium.org/chromium/src.git@ominifix
Patch Set: Address comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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);
Peter Kasting 2012/10/26 01:53:47 OK, Carlos and I chatted some about how this class
Seigo Nonaka 2012/10/26 02:23:10 Sure, thank you! removing macros. On 2012/10/26 01
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.
Peter Kasting 2012/10/26 01:53:47 Nit: forward TSF related -> forwarded TSF-related
Seigo Nonaka 2012/10/26 02:23:10 Done.
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|.
Peter Kasting 2012/10/26 01:53:47 Nit: manager -> thread_manager
Seigo Nonaka 2012/10/26 02:23:10 Done.
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 return S_OK;
113 }
114
115 void TsfEventRouter::TsfEventRouterDelegate::FinalRelease() {}
116
117 void TsfEventRouter::TsfEventRouterDelegate::SetRouter(TsfEventRouter* router) {
118 router_ = router;
119 }
120
121 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::OnEndEdit(
122 ITfContext* context,
123 TfEditCookie read_only_cookie,
124 ITfEditRecord* edit_record) {
125 if (!edit_record || !context)
126 return E_INVALIDARG;
127 if (!router_)
128 return S_OK;
129
130 // |edit_record| can be used to obtain updated ranges in terms of text
131 // contents and/or text attributes. Here we are interested only in text update
132 // so we use TF_GTP_INCL_TEXT and check if there is any range which contains
133 // updated text.
134 base::win::ScopedComPtr<IEnumTfRanges> ranges;
135 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0,
136 ranges.Receive())))
137 return S_OK; // Don't care about failures.
138
139 ULONG fetched_count = 0;
140 base::win::ScopedComPtr<ITfRange> range;
141 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count)))
142 return S_OK; // Don't care about failures.
143
144 // |fetched_count| != 0 means there is at least one range that contains
145 // updated text.
146 if (fetched_count != 0)
147 router_->OnTextUpdated();
148 return S_OK;
149 }
150
151 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::BeginUIElement(
152 DWORD element_id,
153 BOOL* is_show) {
154 if (is_show)
155 *is_show = TRUE; // Without this the UI element will not be shown.
156
157 if (!IsCandidateWindowInternal(element_id))
158 return S_OK;
159
160 std::pair<std::set<DWORD>::iterator, bool> insert_result =
161 open_candidate_window_ids_.insert(element_id);
162 // Don't call if |router_| is null or |element_id| is already handled.
163 if (router_ && insert_result.second)
164 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size());
165
166 return S_OK;
167 }
168
169 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::UpdateUIElement(
170 DWORD element_id) {
171 return S_OK;
172 }
173
174 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::EndUIElement(
175 DWORD element_id) {
176 if ((open_candidate_window_ids_.erase(element_id) != 0) && router_)
177 router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size());
178 return S_OK;
179 }
180
181 void TsfEventRouter::TsfEventRouterDelegate::SetManager(ITfThreadMgr* manager) {
182 EnsureDeassociated();
183 if (manager)
184 Associate(manager);
185 }
186
187 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposing() {
188 return context_ && IsImeComposingInternal(context_);
189 }
190
191 // static
192 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposingInternal(
193 ITfContext* context) {
194 DCHECK(context);
195 base::win::ScopedComPtr<ITfContextComposition> context_composition;
196 if (FAILED(context_composition.QueryFrom(context)))
197 return false;
198 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view;
199 if (FAILED(context_composition->EnumCompositions(
200 enum_composition_view.Receive())))
201 return false;
202 base::win::ScopedComPtr<ITfCompositionView> composition_view;
203 return enum_composition_view->Next(1, composition_view.Receive(),
204 NULL) == S_OK;
205 }
206
207 bool TsfEventRouter::TsfEventRouterDelegate::IsCandidateWindowInternal(
208 DWORD element_id) {
209 DCHECK(ui_element_manager_.get());
210 base::win::ScopedComPtr<ITfUIElement> ui_element;
211 if (FAILED(ui_element_manager_->GetUIElement(element_id,
212 ui_element.Receive())))
213 return false;
214 base::win::ScopedComPtr<ITfCandidateListUIElement> candidate_list_ui_element;
215 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element));
216 }
217
218 void TsfEventRouter::TsfEventRouterDelegate::Associate(
219 ITfThreadMgr* thread_manager) {
220 DCHECK(thread_manager);
221 base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
222 if (FAILED(thread_manager->GetFocus(document_manager.Receive())) ||
223 FAILED(document_manager->GetBase(context_.Receive())) ||
224 FAILED(context_source_.QueryFrom(context_)))
225 return;
226 context_source_->AdviseSink(IID_ITfTextEditSink,
227 static_cast<ITfTextEditSink*>(this),
228 &context_source_cookie_);
229
230 if (FAILED(ui_element_manager_.QueryFrom(thread_manager)) ||
231 FAILED(ui_source_.QueryFrom(ui_element_manager_)))
232 return;
233 ui_source_->AdviseSink(IID_ITfUIElementSink,
234 static_cast<ITfUIElementSink*>(this),
235 &ui_source_cookie_);
236 }
237
238 void TsfEventRouter::TsfEventRouterDelegate::EnsureDeassociated() {
Peter Kasting 2012/10/26 01:53:47 Nit: Only called once, just inline this function i
Seigo Nonaka 2012/10/26 02:23:10 Sure, done and also done for Associate which is al
239 context_.Release();
240
241 if (context_source_) {
242 context_source_->UnadviseSink(context_source_cookie_);
243 context_source_.Release();
244 }
245 context_source_cookie_ = TF_INVALID_COOKIE;
246
247 ui_element_manager_.Release();
248 if (ui_source_) {
249 ui_source_->UnadviseSink(ui_source_cookie_);
250 ui_source_.Release();
251 }
252 ui_source_cookie_ = TF_INVALID_COOKIE;
253 }
254
255 // TsfEventRouter ------------------------------------------------------------
Peter Kasting 2012/10/26 01:53:47 Nit: Add another blank line on both sides of this
Seigo Nonaka 2012/10/26 02:23:10 Done.
256 TsfEventRouter::TsfEventRouter(TsfEventRouterObserver* observer)
257 : observer_(observer),
258 delegate_(NULL) {
259 DCHECK(base::win::IsTsfAwareRequired())
260 << "Do not use TsfEventRouter without TSF environment.";
261 ui::win::CreateATLModuleIfNeeded();
262 if (SUCCEEDED(CComObject<TsfEventRouterDelegate>::CreateInstance(
263 &delegate_))) {
264 delegate_->AddRef();
265 delegate_->SetRouter(this);
266 }
267 } 267 }
268 268
269 TsfEventRouter::~TsfEventRouter() { 269 TsfEventRouter::~TsfEventRouter() {
270 } 270 if (delegate_) {
271 271 delegate_->SetManager(NULL);
272 TsfEventRouter* TsfEventRouter::Create() { 272 delegate_->SetRouter(NULL);
273 return new TsfEventRouterImpl(); 273 delegate_->Release();
274 delegate_ = NULL;
Peter Kasting 2012/10/26 01:53:47 Once you switch back to CComPtr, these last two li
Seigo Nonaka 2012/10/26 02:23:10 Done.
275 }
276 }
277
278 void TsfEventRouter::SetManager(ITfThreadMgr* manager) {
279 delegate_->SetManager(manager);
280 }
281
282 bool TsfEventRouter::IsImeComposing() {
283 return delegate_->IsImeComposing();
284 }
285
286 void TsfEventRouter::OnTextUpdated() {
287 observer_->OnTextUpdated();
288 }
289
290 void TsfEventRouter::OnCandidateWindowCountChanged(size_t window_count) {
291 observer_->OnCandidateWindowCountChanged(window_count);
274 } 292 }
275 293
276 } // namespace ui 294 } // namespace ui
OLDNEW
« ui/base/ime/win/tsf_event_router.h ('K') | « ui/base/ime/win/tsf_event_router.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698