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

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