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

Side by Side Diff: win8/metro_driver/ime/text_service.cc

Issue 83233002: Enable basic IME functionality under Ash on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More task delegation from ChromeAppViewAsh to TextServiceImpl Created 7 years 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
(Empty)
1 // Copyright (c) 2013 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 "win8/metro_driver/ime/text_service.h"
6
7 #include <msctf.h>
8
9 #include "base/win/scoped_variant.h"
10 #include "ui/metro_viewer/ime_types.h"
11 #include "win8/metro_driver/ime/text_service_delegate.h"
12 #include "win8/metro_driver/ime/text_store.h"
13 #include "win8/metro_driver/ime/text_store_delegate.h"
14
15 namespace metro_driver {
ananta 2013/11/27 02:27:44 Can you add some commentary here about how this wh
yukawa 2013/11/27 11:30:04 Done.
16 namespace {
17 typedef TSFTextStore TextStore;
18
19 // Japanese IME expects the default value of this compartment is
20 // TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is
21 // managed per thread, thus setting this value at once is enough. This
22 // value never affects other IMEs except for Japanese.
23 bool InitializeSentenceMode(ITfThreadMgr2* thread_manager,
24 TfClientId client_id) {
25 base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager;
26 if (FAILED(thread_compartment_manager.QueryFrom(thread_manager)))
27 return false;
28 base::win::ScopedComPtr<ITfCompartment> sentence_compartment;
29 if (FAILED(thread_compartment_manager->GetCompartment(
30 GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
31 sentence_compartment.Receive()))) {
32 return false;
33 }
34
35 base::win::ScopedVariant sentence_variant;
36 sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
37 if (FAILED(sentence_compartment->SetValue(client_id, &sentence_variant)))
38 return false;
39 return true;
40 }
41
42 // Initializes |context| as disabled context where IMEs will be disabled.
43 bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) {
44 base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr;
45 if (FAILED(compartment_mgr.QueryFrom(context)))
46 return false;
47
48 base::win::ScopedComPtr<ITfCompartment> disabled_compartment;
49 if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
50 disabled_compartment.Receive()))) {
51 return false;
52 }
53
54 base::win::ScopedVariant variant;
55 variant.Set(1);
56 if (FAILED(disabled_compartment->SetValue(client_id, &variant)))
57 return false;
58
59 base::win::ScopedComPtr<ITfCompartment> empty_context;
60 if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
61 empty_context.Receive()))) {
62 return false;
63 }
64
65 base::win::ScopedVariant empty_context_variant;
66 empty_context_variant.Set(static_cast<int32>(1));
67 if (FAILED(empty_context->SetValue(client_id, &empty_context_variant)))
68 return false;
69
70 return true;
71 }
72
73 scoped_refptr<TextStore> CreateTextStore(const std::vector<int32>& input_scopes,
74 HWND window_handle,
75 TextStoreDelegate* delegate) {
76 if (input_scopes.empty())
77 return NULL;
78 std::vector<InputScope> buffer(input_scopes.size());
79 for (size_t i = 0; i < input_scopes.size(); ++i)
80 buffer[i] = static_cast<InputScope>(input_scopes[i]);
81 return new TextStore(window_handle, buffer, delegate);
82 }
83
84 // A class that manages the lifetime of the event callback registration. When
85 // this object is destroyed, corresponding event callback will be unregistered.
86 class EventSink {
87 public:
88 EventSink(DWORD cookie, base::win::ScopedComPtr<ITfSource> source)
89 : cookie_(cookie),
90 source_(source) {}
91 ~EventSink() {
92 if (!source_ || cookie_ != TF_INVALID_COOKIE)
93 return;
94 source_->UnadviseSink(cookie_);
95 cookie_ = TF_INVALID_COOKIE;
96 source_.Release();
97 }
98
99 private:
100 DWORD cookie_;
101 base::win::ScopedComPtr<ITfSource> source_;
102 DISALLOW_COPY_AND_ASSIGN(EventSink);
103 };
104
105 scoped_ptr<EventSink> CreateTextEditSink(ITfContext* context,
106 ITfTextEditSink* text_store) {
107 if (!text_store)
108 return scoped_ptr<EventSink>();
109 base::win::ScopedComPtr<ITfSource> source;
110 DWORD cookie = TF_INVALID_EDIT_COOKIE;
111 if (FAILED(source.QueryFrom(context)))
112 return scoped_ptr<EventSink>();
113 if (FAILED(source->AdviseSink(IID_ITfTextEditSink, text_store, &cookie)))
114 return scoped_ptr<EventSink>();
115 return scoped_ptr<EventSink>(new EventSink(cookie, source));
116 }
117
118 // A set of objects that should have the same lifetime. This contains the text
119 // buffer (TextStore) implemented by Chrome and corresponding focusable object
120 // (ITfDocumentMgr) managed by TSF. This also manages the lifetime of the event
121 // callback registration between TSF subsystem and TextStore.
122 class DocumentBinding {
123 public:
124 ~DocumentBinding() {
125 }
126
127 static scoped_ptr<DocumentBinding> Create(
128 ITfThreadMgr2* thread_manager,
129 TfClientId client_id,
130 const std::vector<int32>& input_scopes,
131 HWND window_handle,
132 TextStoreDelegate* delegate) {
133 base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
134 if (!thread_manager)
135 return scoped_ptr<DocumentBinding>();
136 if (FAILED(thread_manager->CreateDocumentMgr(document_manager.Receive())))
137 return scoped_ptr<DocumentBinding>();
138
139 // Note: |text_store| can be NULL and it's not an error. It means that
140 // the document expects an IME to be disabled while the document is focused.
141 scoped_refptr<TextStore> text_store = CreateTextStore(input_scopes,
142 window_handle,
143 delegate);
144
145 base::win::ScopedComPtr<ITfContext> context;
146 DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
147 if (FAILED(document_manager->CreateContext(
148 client_id,
149 0,
150 static_cast<ITextStoreACP*>(text_store.get()),
151 context.Receive(),
152 &edit_cookie))) {
153 return scoped_ptr<DocumentBinding>();
154 }
155 if (FAILED(document_manager->Push(context)))
156 return scoped_ptr<DocumentBinding>();
157 const bool is_password_field =
158 std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) !=
159 input_scopes.end();
160 const bool use_disabled_context = input_scopes.empty() || is_password_field;
161 if (use_disabled_context && !InitializeDisabledContext(context, client_id))
162 return scoped_ptr<DocumentBinding>();
163 scoped_ptr<EventSink> text_edit_sink = CreateTextEditSink(context,
164 text_store);
165 return scoped_ptr<DocumentBinding>(
166 new DocumentBinding(text_store,
167 document_manager,
168 text_edit_sink.Pass()));
169 }
170
171 ITfDocumentMgr* document_manager() const {
172 return document_manager_;
173 }
174
175 scoped_refptr<TextStore> text_store() {
176 return text_store_;
177 }
178
179 private:
180 DocumentBinding(scoped_refptr<TextStore> text_store,
181 base::win::ScopedComPtr<ITfDocumentMgr> document_manager,
182 scoped_ptr<EventSink> text_edit_sink)
183 : text_store_(text_store),
184 document_manager_(document_manager),
185 text_edit_sink_(text_edit_sink.Pass()) {}
186
187 scoped_refptr<TextStore> text_store_;
188 base::win::ScopedComPtr<ITfDocumentMgr> document_manager_;
189 base::win::ScopedComPtr<ITfContext> context_;
190 base::win::ScopedComPtr<ITfSource> text_edit_sink_source_;
191 scoped_ptr<EventSink> text_edit_sink_;
192
193 DISALLOW_COPY_AND_ASSIGN(DocumentBinding);
194 };
195
196 class TextServiceImpl : public TextService,
197 public TextStoreDelegate {
198 public:
199 TextServiceImpl(base::win::ScopedComPtr<ITfThreadMgr2> thread_manager,
200 TfClientId client_id,
201 HWND window_handle,
202 TextServiceDelegate* delegate)
203 : client_id_(client_id),
204 window_handle_(window_handle),
205 delegate_(delegate),
206 thread_manager_(thread_manager) {
207 DCHECK_NE(TF_CLIENTID_NULL, client_id);
208 DCHECK(window_handle != NULL);
209 DCHECK(thread_manager_);
210 }
211 virtual ~TextServiceImpl() {
212 thread_manager_->Deactivate();
213 }
214
215 private:
216 // TextService overrides:
217 virtual void TextService::CancelComposition() OVERRIDE {
218 DocumentBinding* document = current_document_.get();
219 if (!document)
220 return;
221 TextStore* text_store = document->text_store();
222 if (!text_store)
223 return;
224 text_store->CancelComposition();
225 }
226
227 virtual void OnDocumentChanged(
228 const std::vector<int32>& input_scopes,
229 const std::vector<metro_viewer::CharacterBounds>& character_bounds)
230 OVERRIDE {
231 bool document_changed = input_scopes_ != input_scopes;
232 input_scopes_ = input_scopes;
233 composition_character_bounds_ = character_bounds;
234 if (document_changed)
235 OnDocumentTypeChanged(input_scopes);
236 }
237
238 virtual void OnWindowActivated() OVERRIDE {
239 if (!current_document_)
240 return;
241 ITfDocumentMgr* document_manager = current_document_->document_manager();
242 if (!document_manager)
243 return;
244 if (FAILED(thread_manager_->SetFocus(document_manager)))
245 return;
246 }
247
248 virtual void OnCompositionChanged(
249 const string16& text,
250 int32 selection_start,
251 int32 selection_end,
252 const std::vector<metro_viewer::UnderlineInfo>& underlines) OVERRIDE {
253 if (!delegate_)
254 return;
255 delegate_->OnCompositionChanged(text,
256 selection_start,
257 selection_end,
258 underlines);
259 }
260
261 virtual void OnTextCommitted(const string16& text) OVERRIDE {
262 if (!delegate_)
263 return;
264 delegate_->OnTextCommitted(text);
265 }
266
267 virtual RECT GetCaretBounds() {
268 if (composition_character_bounds_.empty()) {
269 const RECT rect = {};
270 return rect;
271 }
272 const metro_viewer::CharacterBounds& bounds =
273 composition_character_bounds_[0];
274 POINT left_top = { bounds.left, bounds.top };
275 POINT right_bottom = { bounds.right, bounds.bottom };
276 ClientToScreen(window_handle_, &left_top);
277 ClientToScreen(window_handle_, &right_bottom);
278 const RECT rect = {
279 left_top.x,
280 left_top.y,
281 right_bottom.x,
282 right_bottom.y,
283 };
284 return rect;
285 }
286
287 virtual bool GetCompositionCharacterBounds(uint32 index,
288 RECT* rect) OVERRIDE {
289 if (index >= composition_character_bounds_.size()) {
290 return false;
291 }
292 const metro_viewer::CharacterBounds& bounds =
293 composition_character_bounds_[index];
294 POINT left_top = { bounds.left, bounds.top };
295 POINT right_bottom = { bounds.right, bounds.bottom };
296 ClientToScreen(window_handle_, &left_top);
297 ClientToScreen(window_handle_, &right_bottom);
298 SetRect(rect, left_top.x, left_top.y, right_bottom.x, right_bottom.y);
299 return true;
300 }
301
302 void OnDocumentTypeChanged(const std::vector<int32>& input_scopes) {
303 scoped_ptr<DocumentBinding> new_document =
304 DocumentBinding::Create(thread_manager_.get(),
305 client_id_,
306 input_scopes,
307 window_handle_,
308 this);
309 if (!new_document)
310 return;
311 current_document_.swap(new_document);
312 OnWindowActivated();
313 }
314
315 TfClientId client_id_;
316 HWND window_handle_;
317 TextServiceDelegate* delegate_;
318 scoped_ptr<DocumentBinding> current_document_;
319 base::win::ScopedComPtr<ITfThreadMgr2> thread_manager_;
320 std::vector<int32> input_scopes_;
321 std::vector<metro_viewer::CharacterBounds> composition_character_bounds_;
322
323 DISALLOW_COPY_AND_ASSIGN(TextServiceImpl);
324 };
325
326 } // namespace
327
328 scoped_ptr<TextService>
329 CreateTextService(TextServiceDelegate* delegate, HWND window_handle) {
330 if (!delegate)
331 return scoped_ptr<TextService>();
332 base::win::ScopedComPtr<ITfThreadMgr2> thread_manager;
333 if (FAILED(thread_manager.CreateInstance(CLSID_TF_ThreadMgr)))
334 return scoped_ptr<TextService>();
335 TfClientId client_id = TF_CLIENTID_NULL;
336 if (FAILED(thread_manager->ActivateEx(&client_id, 0)))
337 return scoped_ptr<TextService>();
338 if (!InitializeSentenceMode(thread_manager, client_id)) {
339 thread_manager->Deactivate();
340 return scoped_ptr<TextService>();
341 }
342 return scoped_ptr<TextService>(new TextServiceImpl(thread_manager,
343 client_id,
344 window_handle,
345 delegate));
346 }
347
348 } // namespace metro_driver
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698