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

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
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
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