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

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: Patch for review 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
« no previous file with comments | « ui/base/ime/win/tsf_event_router.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
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
OLDNEW
« no previous file with comments | « 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