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

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: Use ATL for COM object management Created 8 years, 2 months 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 public ITfUIElementSink,
19 public ITfTextEditSink {
20 public:
21 TsfEventRouterImpl()
22 : observer_(NULL),
23 context_source_cookie_(TF_INVALID_COOKIE),
24 ui_source_cookie_(TF_INVALID_COOKIE),
25 ref_count_(0) {}
26
27 virtual ~TsfEventRouterImpl() {}
28
29 // ITfTextEditSink override.
30 virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
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,
63 ITfEditRecord* edit_record) OVERRIDE {
64 if (!edit_record || !context)
65 return E_INVALIDARG;
66 if (!observer_)
67 return S_OK;
68
69 // |edit_record| can be used to obtain updated ranges in terms of text
70 // contents and/or text attributes. Here we are interested only in text
71 // update so we use TF_GTP_INCL_TEXT and check if there is any range which
72 // contains updated text.
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
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
151 private:
152 // Returns true if the given |context| is in composing.
153 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
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
236 // A context associated with this class.
237 base::win::ScopedComPtr<ITfContext> context_;
238
239 // The ITfSource associated with |context_|.
240 base::win::ScopedComPtr<ITfSource> context_source_;
241
242 // The cookie for |context_source_|.
243 DWORD context_source_cookie_;
244
245 // A UiElementMgr associated with this class.
246 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_;
247
248 // The ITfSouce associated with |ui_element_manager_|.
249 base::win::ScopedComPtr<ITfSource> ui_source_;
250
251 // The cookie for |ui_source_|.
252 DWORD ui_source_cookie_;
253
254 // The set of currently opend candidate window ids.
255 std::set<DWORD> open_candidate_window_ids_;
256
257 // The reference count of this instance.
258 volatile LONG ref_count_;
259
260 DISALLOW_COPY_AND_ASSIGN(TsfEventRouterImpl);
261 };
262
263 ///////////////////////////////////////////////////////////////////////////////
264 // TsfEventRouter
265
266 TsfEventRouter::TsfEventRouter() { 18 TsfEventRouter::TsfEventRouter() {
19 ui::win::CreateATLModuleIfNeeded();
20 CComObject<TsfEventRouterDelegate>* delegate = NULL;
21 if (SUCCEEDED(CComObject<TsfEventRouterDelegate>::CreateInstance(
22 &delegate))) {
Peter Kasting 2012/10/24 20:48:11 I believe you have to explicitly AddRef() here, si
cpu_(ooo_6.6-7.5) 2012/10/25 00:43:29 Good catch. This is a bug. I can even guess the re
Seigo Nonaka 2012/10/25 14:37:16 I'm getting confused about what type is should be
23 delegate_ = delegate;
24 }
267 } 25 }
268 26
269 TsfEventRouter::~TsfEventRouter() { 27 TsfEventRouter::~TsfEventRouter() {
270 } 28 if (delegate_)
271 29 delegate_->SetManager(NULL, NULL);
272 TsfEventRouter* TsfEventRouter::Create() { 30 }
273 return new TsfEventRouterImpl(); 31
274 } 32 TsfEventRouter::TsfEventRouterDelegate::TsfEventRouterDelegate()
275 33 : context_source_cookie_(TF_INVALID_COOKIE),
34 ui_source_cookie_(TF_INVALID_COOKIE),
Peter Kasting 2012/10/24 20:48:11 Nit: Indent even
Seigo Nonaka 2012/10/25 14:37:16 Done.
35 observer_(NULL) {}
36
37 TsfEventRouter::TsfEventRouterDelegate::~TsfEventRouterDelegate() {}
38
39 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::OnEndEdit(
Peter Kasting 2012/10/24 20:48:11 Please separate all TsfEventRouterDelegate functio
Seigo Nonaka 2012/10/25 14:37:16 Done.
40 ITfContext* context,
41 TfEditCookie read_only_cookie,
42 ITfEditRecord* edit_record) {
43 if (!edit_record || !context)
44 return E_INVALIDARG;
45 if (!observer_)
46 return S_OK;
47
48 // |edit_record| can be used to obtain updated ranges in terms of text
49 // contents and/or text attributes. Here we are interested only in text update
50 // so we use TF_GTP_INCL_TEXT and check if there is any range which contains
51 // updated text.
52 base::win::ScopedComPtr<IEnumTfRanges> ranges;
53 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, NULL, 0,
54 ranges.Receive()))) {
Peter Kasting 2012/10/24 20:48:11 Nit: {} unnecessary when body is one line (3 place
Seigo Nonaka 2012/10/25 14:37:16 Done.
55 return S_OK; // don't care about failures.
Peter Kasting 2012/10/24 20:48:11 Nit: Capitalize first word (3 places)
Seigo Nonaka 2012/10/25 14:37:16 Done.
56 }
57
58 ULONG fetched_count = 0;
59 base::win::ScopedComPtr<ITfRange> range;
60 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count)))
61 return S_OK; // don't care about failures.
62
63 // |fetched_count| != 0 means there is at least one range that contains
64 // updated texts.
Peter Kasting 2012/10/24 20:48:11 Nit: texts -> text
Seigo Nonaka 2012/10/25 14:37:16 Done.
65 if (fetched_count != 0)
66 observer_->OnTextUpdated();
67 return S_OK;
68 }
69
70 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::BeginUIElement(
71 DWORD element_id,
72 BOOL* is_show) {
73 if (is_show)
74 *is_show = TRUE; // without this the UI element will not be shown.
75
76 if (!IsCandidateWindowInternal(element_id))
77 return S_OK;
78
79 std::pair<std::set<DWORD>::iterator, bool> insert_result =
80 open_candidate_window_ids_.insert(element_id);
81
82 if (!observer_)
Peter Kasting 2012/10/24 20:48:11 Nit: Simpler: if (observer_ && insert_result.se
Seigo Nonaka 2012/10/25 14:37:16 Done.
83 return S_OK;
84
85 // Don't call if |element_id| is already handled.
86 if (!insert_result.second)
87 return S_OK;
88
89 observer_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size());
90
91 return S_OK;
92 }
93
94 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::UpdateUIElement(
95 DWORD element_id) {
96 return S_OK;
97 }
98
99 STDMETHODIMP TsfEventRouter::TsfEventRouterDelegate::EndUIElement(
100 DWORD element_id) {
101 if (open_candidate_window_ids_.erase(element_id) == 0)
Peter Kasting 2012/10/24 20:48:11 Nit: Simpler: if ((open_candidate_window_ids_.e
Seigo Nonaka 2012/10/25 14:37:16 Done.
102 return S_OK;
103
104 if (!observer_)
105 return S_OK;
106
107 observer_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size());
108
109 return S_OK;
110 }
111
112 void TsfEventRouter::TsfEventRouterDelegate::SetManager(ITfThreadMgr* manager,
113 Observer* observer) {
114 EnsureDeassociated();
115 if (manager && observer) {
116 Associate(manager, observer);
117 }
118 }
119
120 void TsfEventRouter::SetManager(ITfThreadMgr* manager, Observer* observer) {
121 delegate_->SetManager(manager, observer);
122 }
123
124 bool TsfEventRouter::IsImeComposing() {
125 return delegate_->IsImeComposing();
126 }
127
128 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposing() {
129 DCHECK(base::win::IsTsfAwareRequired())
Peter Kasting 2012/10/24 20:48:11 Seems like this DCHECK belongs in the constructor
Seigo Nonaka 2012/10/25 14:37:16 Yes, removed. On 2012/10/24 20:48:11, Peter Kastin
130 << "Do not call without TSF environment.";
131 if (!context_)
Peter Kasting 2012/10/24 20:48:11 Nit: Simpler: return context_ && IsImeComposing
Seigo Nonaka 2012/10/25 14:37:16 Done.
132 return false;
133 return IsImeComposingInternal(context_);
134 }
135
136 // static
137 bool TsfEventRouter::TsfEventRouterDelegate::IsImeComposingInternal(
138 ITfContext* context) {
139 DCHECK(base::win::IsTsfAwareRequired())
140 << "Do not call without TSF environment.";
141 DCHECK(context);
Peter Kasting 2012/10/24 20:48:11 Nit: Be consistent about whether precondition DCHE
Seigo Nonaka 2012/10/25 14:37:16 Sure, removing all blank line after DCHECK. On 201
142 base::win::ScopedComPtr<ITfContextComposition> context_composition;
143 if (FAILED(context_composition.QueryFrom(context)))
144 return false;
145 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view;
146 if (FAILED(context_composition->EnumCompositions(
147 enum_composition_view.Receive())))
Peter Kasting 2012/10/24 20:48:11 Nit: indent 4, not 8
Seigo Nonaka 2012/10/25 14:37:16 Done.
148 return false;
149 base::win::ScopedComPtr<ITfCompositionView> composition_view;
150 return
Peter Kasting 2012/10/24 20:48:11 Nit: I suggest breaking as: return enum_composi
Seigo Nonaka 2012/10/25 14:37:16 Sure, done. On 2012/10/24 20:48:11, Peter Kasting
151 enum_composition_view->Next(1, composition_view.Receive(), NULL) == S_OK;
152 }
153
154 bool TsfEventRouter::TsfEventRouterDelegate::IsCandidateWindowInternal(
155 DWORD element_id) {
156 DCHECK(ui_element_manager_.get());
157 base::win::ScopedComPtr<ITfUIElement> ui_element;
158 if (FAILED(ui_element_manager_->GetUIElement(element_id,
159 ui_element.Receive()))) {
160 return false;
161 }
162 base::win::ScopedComPtr<ITfCandidateListUIElement> candidate_list_ui_element;
163 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element));
164 }
165
166 void TsfEventRouter::TsfEventRouterDelegate::Associate(
167 ITfThreadMgr* thread_manager,
168 Observer* observer) {
169 DCHECK(base::win::IsTsfAwareRequired())
170 << "Do not call without TSF environment.";
171 DCHECK(thread_manager);
172
173 base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
174 if (FAILED(thread_manager->GetFocus(document_manager.Receive())))
Peter Kasting 2012/10/24 20:48:11 Nit: Can combine multiple FAILED() calls together
Seigo Nonaka 2012/10/25 14:37:16 Done.
175 return;
176
177 if (FAILED(document_manager->GetBase(context_.Receive())))
178 return;
179 if (FAILED(context_source_.QueryFrom(context_)))
180 return;
181 context_source_->AdviseSink(IID_ITfTextEditSink,
182 static_cast<ITfTextEditSink*>(this),
183 &context_source_cookie_);
184
185 if (FAILED(ui_element_manager_.QueryFrom(thread_manager)))
186 return;
187 if (FAILED(ui_source_.QueryFrom(ui_element_manager_)))
188 return;
189 ui_source_->AdviseSink(IID_ITfUIElementSink,
190 static_cast<ITfUIElementSink*>(this),
191 &ui_source_cookie_);
192 observer_ = observer;
193 }
194
195 void TsfEventRouter::TsfEventRouterDelegate::EnsureDeassociated() {
196 DCHECK(base::win::IsTsfAwareRequired())
197 << "Do not call without TSF environment.";
198
199 context_.Release();
200
201 if (context_source_) {
202 context_source_->UnadviseSink(context_source_cookie_);
203 context_source_.Release();
204 }
205 context_source_cookie_ = TF_INVALID_COOKIE;
206
207 ui_element_manager_.Release();
208 if (ui_source_) {
209 ui_source_->UnadviseSink(ui_source_cookie_);
210 ui_source_.Release();
211 }
212 ui_source_cookie_ = TF_INVALID_COOKIE;
213
214 observer_ = NULL;
215 }
276 } // namespace ui 216 } // 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