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

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

Issue 11148012: Introduce TsfEventRouter. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fix win build break 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
« no previous file with comments | « ui/base/win/tsf_event_router.h ('k') | ui/ui.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/base/win/tsf_event_router.h"
6
7 #include <msctf.h>
8 #include <set>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/win/scoped_comptr.h"
13 #include "base/win/metro.h"
14
15 namespace ui {
16
17 class TsfEventRouterImpl : public TsfEventRouter,
18 public ITfUIElementSink,
19 public ITfTextEditSink {
20 public:
21 TsfEventRouterImpl()
22 : context_source_cookie_(TF_INVALID_COOKIE),
23 ui_source_cookie_(TF_INVALID_COOKIE),
24 ref_count_(0) {}
25
26 virtual ~TsfEventRouterImpl() {}
27
28 // ITfTextEditSink override.
29 virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE {
30 return InterlockedIncrement(&ref_count_);
31 }
32
33 // ITfTextEditSink override.
34 virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE {
35 const LONG count = InterlockedDecrement(&ref_count_);
36 if (!count) {
37 delete this;
38 return 0;
39 }
40 return static_cast<ULONG>(count);
41 }
42
43 // ITfTextEditSink override.
44 virtual STDMETHODIMP QueryInterface(REFIID iid, void** result) OVERRIDE {
45 if (!result)
46 return E_INVALIDARG;
47 if (iid == IID_IUnknown || iid == IID_ITfTextEditSink) {
48 *result = static_cast<ITfTextEditSink*>(this);
49 } else if (iid == IID_ITfUIElementSink) {
50 *result = static_cast<ITfUIElementSink*>(this);
51 } else {
52 *result = NULL;
53 return E_NOINTERFACE;
54 }
55 AddRef();
56 return S_OK;
57 }
58
59 // ITfTextEditSink override.
60 virtual STDMETHODIMP OnEndEdit(ITfContext* context,
61 TfEditCookie read_only_cookie,
62 ITfEditRecord* edit_record) OVERRIDE {
63 if (!edit_record || !context)
64 return E_INVALIDARG;
65 if (text_updated_callback_.is_null())
66 return S_OK;
67
68 // |edit_record| can be used to obtain updated ranges in terms of text
69 // contents and/or text attributes. Here we are interested only in text
70 // update so we use TF_GTP_INCL_TEXT and check if there is any range which
71 // contains updated text.
72 base::win::ScopedComPtr<IEnumTfRanges> ranges;
73 if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT,
74 NULL,
75 0,
76 ranges.Receive()))) {
77 return S_OK; // don't care about failures.
78 }
79
80 ULONG fetched_count = 0;
81 base::win::ScopedComPtr<ITfRange> range;
82 if (FAILED(ranges->Next(1, range.Receive(), &fetched_count)))
83 return S_OK; // don't care about failures.
84
85 // |fetched_count| != 0 means there is at least one range that contains
86 // updated texts.
87 if (fetched_count != 0)
88 text_updated_callback_.Run();
89 return S_OK;
90 }
91
92 // ITfUiElementSink override.
93 virtual STDMETHODIMP BeginUIElement(DWORD element_id, BOOL* is_show) {
94 if (is_show)
95 *is_show = TRUE; // without this the UI element will not be shown.
96
97 if (!IsCandidateWindowInternal(element_id))
98 return S_OK;
99
100 std::pair<std::set<DWORD>::iterator, bool> insert_result =
101 open_candidate_window_ids_.insert(element_id);
102
103 if (candidat_window_count_changed_callback_.is_null())
104 return S_OK;
105
106 // Don't call if |element_id| is already handled.
107 if (!insert_result.second)
108 return S_OK;
109
110 candidat_window_count_changed_callback_.Run(
111 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 (candidat_window_count_changed_callback_.is_null())
127 return S_OK;
128
129 candidat_window_count_changed_callback_.Run(
130 open_candidate_window_ids_.size());
131
132 return S_OK;
133 }
134
135 // TsfEventRouter override.
136 virtual void SetManager(ITfThreadMgr* manager) OVERRIDE {
137 EnsureDeassociated();
138 if (manager)
139 Associate(manager);
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 // TsfEventRouter override.
152 virtual void SetTextUpdatedCallback(
153 const TextUpdatedCallback& callback) OVERRIDE {
154 text_updated_callback_ = callback;
155 }
156
157 // TsfEventRouter override.
158 virtual void SetCandidateWindowStatusChangedCallback(
159 const CandidateWindowCountChangedCallback& callback) OVERRIDE {
160 candidat_window_count_changed_callback_ = callback;
161 }
162
163 private:
164 // Returns true if the given |context| is in composing.
165 static bool IsImeComposingInternal(ITfContext* context) {
166 DCHECK(base::win::IsTsfAwareRequired())
167 << "Do not call without TSF environment.";
168 DCHECK(context);
169 base::win::ScopedComPtr<ITfContextComposition> context_composition;
170 if (FAILED(context_composition.QueryFrom(context)))
171 return false;
172 base::win::ScopedComPtr<IEnumITfCompositionView> enum_composition_view;
173 if (FAILED(context_composition->EnumCompositions(
174 enum_composition_view.Receive())))
175 return false;
176 base::win::ScopedComPtr<ITfCompositionView> composition_view;
177 return enum_composition_view->Next(1, composition_view.Receive(),
178 NULL) == S_OK;
179 }
180
181 // Returns true if the given |element_id| represents candidate window.
182 bool IsCandidateWindowInternal(DWORD element_id) {
183 DCHECK(ui_element_manager_.get());
184 base::win::ScopedComPtr<ITfUIElement> ui_element;
185 if (FAILED(ui_element_manager_->GetUIElement(element_id,
186 ui_element.Receive()))) {
187 return false;
188 }
189 base::win::ScopedComPtr<ITfCandidateListUIElement>
190 candidate_list_ui_element;
191 return SUCCEEDED(candidate_list_ui_element.QueryFrom(ui_element));
192 }
193
194 // Associates this class with specified |manager|.
195 void Associate(ITfThreadMgr* thread_manager) {
196 DCHECK(base::win::IsTsfAwareRequired())
197 << "Do not call without TSF environment.";
198 DCHECK(thread_manager);
199
200 base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
201 if (FAILED(thread_manager->GetFocus(document_manager.Receive())))
202 return;
203
204 if (FAILED(document_manager->GetBase(context_.Receive())))
205 return;
206 if (FAILED(context_source_.QueryFrom(context_)))
207 return;
208 context_source_->AdviseSink(IID_ITfTextEditSink,
209 static_cast<ITfTextEditSink*>(this),
210 &context_source_cookie_);
211
212 if (FAILED(ui_element_manager_.QueryFrom(thread_manager)))
213 return;
214 if (FAILED(ui_source_.QueryFrom(ui_element_manager_)))
215 return;
216 ui_source_->AdviseSink(IID_ITfUIElementSink,
217 static_cast<ITfUIElementSink*>(this),
218 &ui_source_cookie_);
219 }
220
221 // Resets the association, this function is safe to call if there is no
222 // associated thread manager.
223 void EnsureDeassociated() {
224 DCHECK(base::win::IsTsfAwareRequired())
225 << "Do not call without TSF environment.";
226
227 context_.Release();
228
229 if (context_source_) {
230 context_source_->UnadviseSink(context_source_cookie_);
231 context_source_.Release();
232 }
233 context_source_cookie_ = TF_INVALID_COOKIE;
234
235 ui_element_manager_.Release();
236 if (ui_source_) {
237 ui_source_->UnadviseSink(ui_source_cookie_);
238 ui_source_.Release();
239 }
240 ui_source_cookie_ = TF_INVALID_COOKIE;
241 }
242
243 // Callback function fired when the text contents are updated.
244 TextUpdatedCallback text_updated_callback_;
245
246 CandidateWindowCountChangedCallback candidat_window_count_changed_callback_;
247
248 // A context associated with this class.
249 base::win::ScopedComPtr<ITfContext> context_;
250
251 // The ITfSource associated with |context_|.
252 base::win::ScopedComPtr<ITfSource> context_source_;
253
254 // The cookie for |context_source_|.
255 DWORD context_source_cookie_;
256
257 // A UiElementMgr associated with this class.
258 base::win::ScopedComPtr<ITfUIElementMgr> ui_element_manager_;
259
260 // The ITfSouce associated with |ui_element_manager_|.
261 base::win::ScopedComPtr<ITfSource> ui_source_;
262
263 // The cookie for |ui_source_|.
264 DWORD ui_source_cookie_;
265
266 // The set of currently opend candidate window ids.
267 std::set<DWORD> open_candidate_window_ids_;
268
269 // The reference count of this instance.
270 volatile LONG ref_count_;
271
272 DISALLOW_COPY_AND_ASSIGN(TsfEventRouterImpl);
273 };
274
275 ///////////////////////////////////////////////////////////////////////////////
276 // TsfEventRouter
277
278 TsfEventRouter::TsfEventRouter() {
279 }
280
281 TsfEventRouter::~TsfEventRouter() {
282 }
283
284 TsfEventRouter* TsfEventRouter::Create() {
285 return new TsfEventRouterImpl();
286 }
287
288 } // namespace ui
OLDNEW
« no previous file with comments | « ui/base/win/tsf_event_router.h ('k') | ui/ui.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698