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

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

Issue 10831403: TsfTextStore implementation. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 3 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_text_store.h ('k') | ui/base/win/tsf_text_store_unittest.cc » ('j') | 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/win/tsf_text_store.h" 5 #include "ui/base/win/tsf_text_store.h"
6 6
7 #include <OleCtl.h> 7 #include <OleCtl.h>
8 #include "base/logging.h" 8
9 #include "base/win/scoped_variant.h"
10 #include "ui/base/ime/text_input_client.h"
11 #include "ui/gfx/rect.h"
9 12
10 namespace ui { 13 namespace ui {
14 namespace {
15
16 // We support only one view.
17 const TsViewCookie kViewCookie = 1;
18
19 } // namespace
11 20
12 TsfTextStore::TsfTextStore() 21 TsfTextStore::TsfTextStore()
13 : ref_count_(0), 22 : ref_count_(0),
14 text_store_acp_sink_mask_(0) { 23 text_store_acp_sink_mask_(0),
24 window_handle_(NULL),
25 text_input_client_(NULL),
26 committed_size_(0),
27 edit_flag_(false),
28 current_lock_type_(0) {
29 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) {
30 LOG(FATAL) << "Failed to initialize CategoryMgr.";
31 return;
32 }
33 if (FAILED(display_attribute_manager_.CreateInstance(
34 CLSID_TF_DisplayAttributeMgr))) {
35 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr.";
36 return;
37 }
15 } 38 }
16 39
17 TsfTextStore::~TsfTextStore() { 40 TsfTextStore::~TsfTextStore() {
18 } 41 }
19 42
20 ULONG STDMETHODCALLTYPE TsfTextStore::AddRef() { 43 ULONG STDMETHODCALLTYPE TsfTextStore::AddRef() {
21 return InterlockedIncrement(&ref_count_); 44 return InterlockedIncrement(&ref_count_);
22 } 45 }
23 46
24 ULONG STDMETHODCALLTYPE TsfTextStore::Release() { 47 ULONG STDMETHODCALLTYPE TsfTextStore::Release() {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 89
67 STDMETHODIMP TsfTextStore::FindNextAttrTransition( 90 STDMETHODIMP TsfTextStore::FindNextAttrTransition(
68 LONG acp_start, 91 LONG acp_start,
69 LONG acp_halt, 92 LONG acp_halt,
70 ULONG num_filter_attributes, 93 ULONG num_filter_attributes,
71 const TS_ATTRID* filter_attributes, 94 const TS_ATTRID* filter_attributes,
72 DWORD flags, 95 DWORD flags,
73 LONG* acp_next, 96 LONG* acp_next,
74 BOOL* found, 97 BOOL* found,
75 LONG* found_offset) { 98 LONG* found_offset) {
76 NOTIMPLEMENTED(); 99 if (!acp_next || !found || !found_offset)
77 return E_NOTIMPL; 100 return E_INVALIDARG;
101 // We don't support any attributes.
102 // So we always return "not found".
103 *acp_next = 0;
104 *found = FALSE;
105 *found_offset = 0;
106 return S_OK;
78 } 107 }
79 108
80 STDMETHODIMP TsfTextStore::GetACPFromPoint( 109 STDMETHODIMP TsfTextStore::GetACPFromPoint(
81 TsViewCookie view_cookie, 110 TsViewCookie view_cookie,
82 const POINT* point, 111 const POINT* point,
83 DWORD flags, 112 DWORD flags,
84 LONG* acp) { 113 LONG* acp) {
85 NOTIMPLEMENTED(); 114 NOTIMPLEMENTED();
86 return E_NOTIMPL; 115 return E_NOTIMPL;
87 } 116 }
88 117
89 STDMETHODIMP TsfTextStore::GetActiveView(TsViewCookie* view_cookie) { 118 STDMETHODIMP TsfTextStore::GetActiveView(TsViewCookie* view_cookie) {
90 NOTIMPLEMENTED();
91 if (!view_cookie) 119 if (!view_cookie)
92 return E_INVALIDARG; 120 return E_INVALIDARG;
121 // We support only one view.
122 *view_cookie = kViewCookie;
93 return S_OK; 123 return S_OK;
94 } 124 }
95 125
96 STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos, 126 STDMETHODIMP TsfTextStore::GetEmbedded(LONG acp_pos,
97 REFGUID service, 127 REFGUID service,
98 REFIID iid, 128 REFIID iid,
99 IUnknown** unknown) { 129 IUnknown** unknown) {
130 // We don't support any embedded objects.
100 NOTIMPLEMENTED(); 131 NOTIMPLEMENTED();
101 if (!unknown) 132 if (!unknown)
102 return E_INVALIDARG; 133 return E_INVALIDARG;
103 *unknown = NULL; 134 *unknown = NULL;
104 return E_NOTIMPL; 135 return E_NOTIMPL;
105 } 136 }
106 137
107 STDMETHODIMP TsfTextStore::GetEndACP(LONG* acp) { 138 STDMETHODIMP TsfTextStore::GetEndACP(LONG* acp) {
108 NOTIMPLEMENTED();
109 if (!acp) 139 if (!acp)
110 return E_INVALIDARG; 140 return E_INVALIDARG;
111 return E_NOTIMPL; 141 if (!HasReadLock())
142 return TS_E_NOLOCK;
143 *acp = string_buffer_.size();
144 return S_OK;
112 } 145 }
113 146
114 STDMETHODIMP TsfTextStore::GetFormattedText(LONG acp_start, LONG acp_end, 147 STDMETHODIMP TsfTextStore::GetFormattedText(LONG acp_start, LONG acp_end,
115 IDataObject** data_object) { 148 IDataObject** data_object) {
116 NOTIMPLEMENTED(); 149 NOTIMPLEMENTED();
117 return E_NOTIMPL; 150 return E_NOTIMPL;
118 } 151 }
119 152
120 STDMETHODIMP TsfTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { 153 STDMETHODIMP TsfTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) {
121 NOTIMPLEMENTED(); 154 NOTIMPLEMENTED();
122 if (!rect) 155 if (!rect)
123 return E_INVALIDARG; 156 return E_INVALIDARG;
124 SetRect(rect, 0, 0, 0, 0); 157 SetRect(rect, 0, 0, 0, 0);
125 return S_OK; 158 return S_OK;
126 } 159 }
127 160
128 STDMETHODIMP TsfTextStore::GetSelection(ULONG selection_index, 161 STDMETHODIMP TsfTextStore::GetSelection(ULONG selection_index,
129 ULONG selection_buffer_size, 162 ULONG selection_buffer_size,
130 TS_SELECTION_ACP* selection_buffer, 163 TS_SELECTION_ACP* selection_buffer,
131 ULONG* fetched_count) { 164 ULONG* fetched_count) {
132 NOTIMPLEMENTED();
133 if (!selection_buffer) 165 if (!selection_buffer)
134 return E_INVALIDARG; 166 return E_INVALIDARG;
135 if (!fetched_count) 167 if (!fetched_count)
136 return E_INVALIDARG; 168 return E_INVALIDARG;
137 return E_NOTIMPL; 169 if (!HasReadLock())
170 return TS_E_NOLOCK;
171 *fetched_count = 0;
172 if ((selection_buffer_size > 0) &&
173 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) {
174 selection_buffer[0].acpStart = selection_.start();
175 selection_buffer[0].acpEnd = selection_.end();
176 selection_buffer[0].style.ase = TS_AE_END;
177 selection_buffer[0].style.fInterimChar = FALSE;
178 *fetched_count = 1;
179 }
180 return S_OK;
138 } 181 }
139 182
140 STDMETHODIMP TsfTextStore::GetStatus(TS_STATUS* status) { 183 STDMETHODIMP TsfTextStore::GetStatus(TS_STATUS* status) {
141 NOTIMPLEMENTED();
142 if (!status) 184 if (!status)
143 return E_INVALIDARG; 185 return E_INVALIDARG;
144 return E_NOTIMPL; 186
187 status->dwDynamicFlags = 0;
188 // We don't support hidden text.
189 status->dwStaticFlags = TS_SS_NOHIDDENTEXT;
190
191 return S_OK;
145 } 192 }
146 193
147 STDMETHODIMP TsfTextStore::GetText(LONG acp_start, 194 STDMETHODIMP TsfTextStore::GetText(LONG acp_start,
148 LONG acp_end, 195 LONG acp_end,
149 wchar_t* text_buffer, 196 wchar_t* text_buffer,
150 ULONG text_buffer_size, 197 ULONG text_buffer_size,
151 ULONG* text_buffer_copied, 198 ULONG* text_buffer_copied,
152 TS_RUNINFO* run_info_buffer, 199 TS_RUNINFO* run_info_buffer,
153 ULONG run_info_buffer_size, 200 ULONG run_info_buffer_size,
154 ULONG* run_info_buffer_copied, 201 ULONG* run_info_buffer_copied,
155 LONG* next_acp) { 202 LONG* next_acp) {
156 NOTIMPLEMENTED();
157 if (!text_buffer_copied || !run_info_buffer_copied) 203 if (!text_buffer_copied || !run_info_buffer_copied)
158 return E_INVALIDARG; 204 return E_INVALIDARG;
159 if (!text_buffer && text_buffer_size != 0) 205 if (!text_buffer && text_buffer_size != 0)
160 return E_INVALIDARG; 206 return E_INVALIDARG;
161 if (!run_info_buffer && run_info_buffer_size != 0) 207 if (!run_info_buffer && run_info_buffer_size != 0)
162 return E_INVALIDARG; 208 return E_INVALIDARG;
163 return E_NOTIMPL; 209 if (!HasReadLock())
210 return TF_E_NOLOCK;
211 const LONG string_buffer_size = string_buffer_.size();
212 if (acp_end == -1)
213 acp_end = string_buffer_size;
214 if (!((0 <= acp_start) &&
215 (acp_start <= acp_end) &&
216 (acp_end <= string_buffer_size))) {
217 return TF_E_INVALIDPOS;
218 }
219 acp_end = std::min(acp_end, acp_start + static_cast<LONG>(text_buffer_size));
220 *text_buffer_copied = acp_end - acp_start;
221
222 const string16& result =
223 string_buffer_.substr(acp_start, *text_buffer_copied);
224 for (size_t i = 0; i < result.size(); ++i) {
225 text_buffer[i] = result[i];
226 }
227
228 if (run_info_buffer_size) {
229 run_info_buffer[0].uCount = *text_buffer_copied;
230 run_info_buffer[0].type = TS_RT_PLAIN;
231 *run_info_buffer_copied = 1;
232 }
233
234 *next_acp = acp_end;
235 return S_OK;
164 } 236 }
165 237
166 STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie, 238 STDMETHODIMP TsfTextStore::GetTextExt(TsViewCookie view_cookie,
167 LONG acp_start, 239 LONG acp_start,
168 LONG acp_end, 240 LONG acp_end,
169 RECT* rect, 241 RECT* rect,
170 BOOL* clipped) { 242 BOOL* clipped) {
171 NOTIMPLEMENTED(); 243 if (!rect || !clipped)
172 return E_NOTIMPL; 244 return E_INVALIDARG;
245 if (!text_input_client_)
246 return E_UNEXPECTED;
247 if (view_cookie != kViewCookie)
248 return E_INVALIDARG;
249 if (!HasReadLock())
250 return TS_E_NOLOCK;
251 if (!((static_cast<LONG>(committed_size_) <= acp_start) &&
252 (acp_start <= acp_end) &&
253 (acp_end <= static_cast<LONG>(string_buffer_.size())))) {
254 return TS_E_INVALIDPOS;
255 }
256
257 gfx::Rect result;
258 gfx::Rect tmp_rect;
259 const uint32 start_pos = acp_start - committed_size_;
260 const uint32 end_pos = acp_end - committed_size_;
261
262 if (start_pos == end_pos) {
263 // According to MSDN document, if |acp_start| and |acp_end| are equal it is
264 // OK to just return TS_E_INVALIDARG.
265 // http://msdn.microsoft.com/en-us/library/ms538435
266 // But when using Pinin IME of Windows 8, this method is called with the
267 // equal values of |acp_start| and |acp_end|. So we handle this condition.
268 if (start_pos == 0) {
269 if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) {
270 result = tmp_rect;
271 result.set_width(0);
272 } else if (string_buffer_.size() == committed_size_) {
273 result = text_input_client_->GetCaretBounds();
274 } else {
275 return TS_E_NOLAYOUT;
276 }
277 } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1,
278 &tmp_rect)) {
279 result.set_x(tmp_rect.right());
280 result.set_y(tmp_rect.y());
281 result.set_width(0);
282 result.set_height(tmp_rect.height());
283 } else {
284 return TS_E_NOLAYOUT;
285 }
286 } else {
287 if (!text_input_client_->GetCompositionCharacterBounds(start_pos, &result))
288 return TS_E_NOLAYOUT;
289
290 for (uint32 i = start_pos + 1; i < end_pos; ++i) {
291 if (!text_input_client_->GetCompositionCharacterBounds(i, &tmp_rect))
292 return TS_E_NOLAYOUT;
293 result = result.Union(tmp_rect);
294 }
295 }
296
297 *rect = result.ToRECT();
298 *clipped = FALSE;
299 return S_OK;
173 } 300 }
174 301
175 STDMETHODIMP TsfTextStore::GetWnd(TsViewCookie view_cookie, 302 STDMETHODIMP TsfTextStore::GetWnd(TsViewCookie view_cookie,
176 HWND* window_handle) { 303 HWND* window_handle) {
177 NOTIMPLEMENTED(); 304 if (!window_handle)
178 return E_NOTIMPL; 305 return E_INVALIDARG;
306 if (view_cookie != kViewCookie)
307 return E_INVALIDARG;
308 *window_handle = window_handle_;
309 return S_OK;
179 } 310 }
180 311
181 STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags, 312 STDMETHODIMP TsfTextStore::InsertEmbedded(DWORD flags,
182 LONG acp_start, 313 LONG acp_start,
183 LONG acp_end, 314 LONG acp_end,
184 IDataObject* data_object, 315 IDataObject* data_object,
185 TS_TEXTCHANGE* change) { 316 TS_TEXTCHANGE* change) {
317 // We don't support any embedded objects.
186 NOTIMPLEMENTED(); 318 NOTIMPLEMENTED();
187 return E_NOTIMPL; 319 return E_NOTIMPL;
188 } 320 }
189 321
190 STDMETHODIMP TsfTextStore::InsertEmbeddedAtSelection(DWORD flags, 322 STDMETHODIMP TsfTextStore::InsertEmbeddedAtSelection(DWORD flags,
191 IDataObject* data_object, 323 IDataObject* data_object,
192 LONG* acp_start, 324 LONG* acp_start,
193 LONG* acp_end, 325 LONG* acp_end,
194 TS_TEXTCHANGE* change) { 326 TS_TEXTCHANGE* change) {
327 // We don't support any embedded objects.
195 NOTIMPLEMENTED(); 328 NOTIMPLEMENTED();
196 return E_NOTIMPL; 329 return E_NOTIMPL;
197 } 330 }
198 331
199 STDMETHODIMP TsfTextStore::InsertTextAtSelection(DWORD flags, 332 STDMETHODIMP TsfTextStore::InsertTextAtSelection(DWORD flags,
200 const wchar_t* text_buffer, 333 const wchar_t* text_buffer,
201 ULONG text_buffer_size, 334 ULONG text_buffer_size,
202 LONG* acp_start, 335 LONG* acp_start,
203 LONG* acp_end, 336 LONG* acp_end,
204 TS_TEXTCHANGE* text_change) { 337 TS_TEXTCHANGE* text_change) {
205 NOTIMPLEMENTED(); 338 const LONG start_pos = selection_.start();
206 return E_NOTIMPL; 339 const LONG end_pos = selection_.end();
340 const LONG new_end_pos = start_pos + text_buffer_size;
341
342 if (flags & TS_IAS_QUERYONLY) {
343 if (!HasReadLock())
344 return TS_E_NOLOCK;
345 if (acp_start)
346 *acp_start = start_pos;
347 if (acp_end) {
348 *acp_end = new_end_pos;
349 }
350 return S_OK;
351 }
352
353 if (!HasReadWriteLock())
354 return TS_E_NOLOCK;
355 if (!text_buffer)
356 return E_INVALIDARG;
357
358 DCHECK_LE(start_pos, end_pos);
359 string_buffer_ = string_buffer_.substr(0, start_pos) +
360 string16(text_buffer, text_buffer + text_buffer_size) +
361 string_buffer_.substr(end_pos);
362 if (acp_start)
363 *acp_start = start_pos;
364 if (acp_end)
365 *acp_end = new_end_pos;
366 if (text_change) {
367 text_change->acpStart = start_pos;
368 text_change->acpOldEnd = end_pos;
369 text_change->acpNewEnd = new_end_pos;
370 }
371 selection_.set_start(start_pos);
372 selection_.set_end(new_end_pos);
373 return S_OK;
207 } 374 }
208 375
209 STDMETHODIMP TsfTextStore::QueryInsert( 376 STDMETHODIMP TsfTextStore::QueryInsert(
210 LONG acp_test_start, 377 LONG acp_test_start,
211 LONG acp_test_end, 378 LONG acp_test_end,
212 ULONG text_size, 379 ULONG text_size,
213 LONG* acp_result_start, 380 LONG* acp_result_start,
214 LONG* acp_result_end) { 381 LONG* acp_result_end) {
215 NOTIMPLEMENTED(); 382 if (!((static_cast<LONG>(committed_size_) <= acp_test_start) &&
216 return E_NOTIMPL; 383 (acp_test_start <= acp_test_end) &&
384 (acp_test_end <= static_cast<LONG>(string_buffer_.size())))) {
385 return E_INVALIDARG;
386 }
387 if (acp_result_start)
388 *acp_result_start = acp_test_start;
389 if (acp_result_end)
390 *acp_result_end = acp_test_start + text_size;
391 return S_OK;
217 } 392 }
218 393
219 STDMETHODIMP TsfTextStore::QueryInsertEmbedded(const GUID* service, 394 STDMETHODIMP TsfTextStore::QueryInsertEmbedded(const GUID* service,
220 const FORMATETC* format, 395 const FORMATETC* format,
221 BOOL* insertable) { 396 BOOL* insertable) {
222 NOTIMPLEMENTED(); 397 // We don't support any embedded objects.
223 return E_NOTIMPL; 398 if (insertable)
399 *insertable = FALSE;
400 return S_OK;
224 } 401 }
225 402
226 STDMETHODIMP TsfTextStore::RequestAttrsAtPosition( 403 STDMETHODIMP TsfTextStore::RequestAttrsAtPosition(
227 LONG acp_pos, 404 LONG acp_pos,
228 ULONG attribute_buffer_size, 405 ULONG attribute_buffer_size,
229 const TS_ATTRID* attribute_buffer, 406 const TS_ATTRID* attribute_buffer,
230 DWORD flags) { 407 DWORD flags) {
231 NOTIMPLEMENTED(); 408 // We don't support any document attributes.
232 return E_NOTIMPL; 409 // This method just returns S_OK, and the subsequently called
410 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
411 return S_OK;
233 } 412 }
234 413
235 STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition( 414 STDMETHODIMP TsfTextStore::RequestAttrsTransitioningAtPosition(
236 LONG acp_pos, 415 LONG acp_pos,
237 ULONG attribute_buffer_size, 416 ULONG attribute_buffer_size,
238 const TS_ATTRID* attribute_buffer, 417 const TS_ATTRID* attribute_buffer,
239 DWORD flags) { 418 DWORD flags) {
240 NOTIMPLEMENTED(); 419 // We don't support any document attributes.
241 return E_NOTIMPL; 420 // This method just returns S_OK, and the subsequently called
421 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
422 return S_OK;
242 } 423 }
243 424
244 STDMETHODIMP TsfTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { 425 STDMETHODIMP TsfTextStore::RequestLock(DWORD lock_flags, HRESULT* result) {
245 NOTIMPLEMENTED(); 426 if (!text_store_acp_sink_.get())
246 return E_NOTIMPL; 427 return E_FAIL;
428 if (!result)
429 return E_INVALIDARG;
430
431 if (current_lock_type_ != 0) {
432 if (lock_flags & TS_LF_SYNC) {
433 // Can't lock synchronously.
434 *result = TS_E_SYNCHRONOUS;
435 return S_OK;
436 }
437 // Queue the lock request.
438 lock_queue_.push_back(lock_flags & TS_LF_READWRITE);
439 *result = TS_S_ASYNC;
440 return S_OK;
441 }
442
443 // Lock
444 current_lock_type_ = (lock_flags & TS_LF_READWRITE);
445
446 edit_flag_ = false;
447 const size_t last_committed_size = committed_size_;
448
449 // Grant the lock.
450 *result = text_store_acp_sink_->OnLockGranted(current_lock_type_);
451
452 // Unlock
453 current_lock_type_ = 0;
454
455 // Handles the pending lock requests.
456 while (!lock_queue_.empty()) {
457 current_lock_type_ = lock_queue_.front();
458 lock_queue_.pop_front();
459 text_store_acp_sink_->OnLockGranted(current_lock_type_);
460 current_lock_type_ = 0;
461 }
462
463 if (!edit_flag_) {
464 return S_OK;
465 }
466
467 // If the text store is edited in OnLockGranted(), we may need to call
468 // TextInputClient::InsertText() or TextInputClient::SetCompositionText().
469 const size_t new_committed_size = committed_size_;
470 const string16& new_committed_string =
471 string_buffer_.substr(last_committed_size,
472 new_committed_size - last_committed_size);
473 const string16& composition_string =
474 string_buffer_.substr(new_committed_size);
475
476 // If there is new committed string, calls TextInputClient::InsertText().
477 if ((!new_committed_string.empty()) && text_input_client_) {
478 text_input_client_->InsertText(new_committed_string);
479 }
480
481 // Calls TextInputClient::SetCompositionText().
482 CompositionText composition_text;
483 composition_text.text = composition_string;
484 composition_text.underlines = composition_undelines_;
485 // Adjusts the offset.
486 for (size_t i = 0; i < composition_text.underlines.size(); ++i) {
487 composition_text.underlines[i].start_offset -= new_committed_size;
488 composition_text.underlines[i].end_offset -= new_committed_size;
489 }
490 if (selection_.start() < new_committed_size) {
491 composition_text.selection.set_start(0);
492 } else {
493 composition_text.selection.set_start(
494 selection_.start() - new_committed_size);
495 }
496 if (selection_.end() < new_committed_size) {
497 composition_text.selection.set_end(0);
498 } else {
499 composition_text.selection.set_end(selection_.end() - new_committed_size);
500 }
501 if (text_input_client_)
502 text_input_client_->SetCompositionText(composition_text);
503
504 // If there is no composition string, clear the text store status.
505 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange().
506 if ((composition_string.empty()) && (new_committed_size != 0)) {
507 string_buffer_.clear();
508 committed_size_ = 0;
509 selection_.set_start(0);
510 selection_.set_end(0);
511 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
512 text_store_acp_sink_->OnSelectionChange();
513 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
514 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
515 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
516 TS_TEXTCHANGE textChange;
517 textChange.acpStart = 0;
518 textChange.acpOldEnd = new_committed_size;
519 textChange.acpNewEnd = 0;
520 text_store_acp_sink_->OnTextChange(0, &textChange);
521 }
522 }
523
524 return S_OK;
247 } 525 }
248 526
249 STDMETHODIMP TsfTextStore::RequestSupportedAttrs( 527 STDMETHODIMP TsfTextStore::RequestSupportedAttrs(
250 DWORD flags, 528 DWORD flags,
251 ULONG attribute_buffer_size, 529 ULONG attribute_buffer_size,
252 const TS_ATTRID* attribute_buffer) { 530 const TS_ATTRID* attribute_buffer) {
253 NOTIMPLEMENTED(); 531 // We don't support any document attributes.
254 return E_NOTIMPL; 532 // This method just returns S_OK, and the subsequently called
533 // RetrieveRequestedAttrs() returns 0 as the number of supported attributes.
534 return S_OK;
255 } 535 }
256 536
257 STDMETHODIMP TsfTextStore::RetrieveRequestedAttrs( 537 STDMETHODIMP TsfTextStore::RetrieveRequestedAttrs(
258 ULONG attribute_buffer_size, 538 ULONG attribute_buffer_size,
259 TS_ATTRVAL* attribute_buffer, 539 TS_ATTRVAL* attribute_buffer,
260 ULONG* attribute_buffer_copied) { 540 ULONG* attribute_buffer_copied) {
261 NOTIMPLEMENTED(); 541 if (attribute_buffer_copied == NULL)
262 return E_NOTIMPL; 542 return E_INVALIDARG;
543 // We don't support any document attributes.
544 attribute_buffer_copied = 0;
545 return S_OK;
263 } 546 }
264 547
265 STDMETHODIMP TsfTextStore::SetSelection( 548 STDMETHODIMP TsfTextStore::SetSelection(
266 ULONG selection_buffer_size, 549 ULONG selection_buffer_size,
267 const TS_SELECTION_ACP* selection_buffer) { 550 const TS_SELECTION_ACP* selection_buffer) {
268 NOTIMPLEMENTED(); 551 if (!HasReadWriteLock())
269 return E_NOTIMPL; 552 return TF_E_NOLOCK;
553 if (selection_buffer_size > 0) {
554 const LONG start_pos = selection_buffer[0].acpStart;
555 const LONG end_pos = selection_buffer[0].acpEnd;
556 if (!((static_cast<LONG>(committed_size_) <= start_pos) &&
557 (start_pos <= end_pos) &&
558 (end_pos <= static_cast<LONG>(string_buffer_.size())))) {
559 return TF_E_INVALIDPOS;
560 }
561 selection_.set_start(start_pos);
562 selection_.set_end(end_pos);
563 }
564 return S_OK;
270 } 565 }
271 566
272 STDMETHODIMP TsfTextStore::SetText(DWORD flags, 567 STDMETHODIMP TsfTextStore::SetText(DWORD flags,
273 LONG acp_start, 568 LONG acp_start,
274 LONG acp_end, 569 LONG acp_end,
275 const wchar_t* text_buffer, 570 const wchar_t* text_buffer,
276 ULONG text_buffer_size, 571 ULONG text_buffer_size,
277 TS_TEXTCHANGE* text_change) { 572 TS_TEXTCHANGE* text_change) {
278 NOTIMPLEMENTED(); 573 if (!HasReadWriteLock())
279 return E_NOTIMPL; 574 return TS_E_NOLOCK;
575 if (!((static_cast<LONG>(committed_size_) <= acp_start) &&
576 (acp_start <= acp_end) &&
577 (acp_end <= static_cast<LONG>(string_buffer_.size())))) {
578 return TS_E_INVALIDPOS;
579 }
580
581 TS_SELECTION_ACP selection;
582 selection.acpStart = acp_start;
583 selection.acpEnd = acp_end;
584 selection.style.ase = TS_AE_NONE;
585 selection.style.fInterimChar = 0;
586 if (SetSelection(1, &selection) != S_OK)
587 return E_UNEXPECTED;
588
589 TS_TEXTCHANGE change;
590 if (InsertTextAtSelection(0, text_buffer, text_buffer_size,
591 &acp_start, &acp_end, &change) != S_OK) {
592 return E_UNEXPECTED;
593 }
594 if (text_change)
595 *text_change = change;
596
597 return S_OK;
280 } 598 }
281 599
282 STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) { 600 STDMETHODIMP TsfTextStore::UnadviseSink(IUnknown* unknown) {
283 if (!text_store_acp_sink_.IsSameObject(unknown)) 601 if (!text_store_acp_sink_.IsSameObject(unknown))
284 return CONNECT_E_NOCONNECTION; 602 return CONNECT_E_NOCONNECTION;
285 text_store_acp_sink_.Release(); 603 text_store_acp_sink_.Release();
286 text_store_acp_sink_mask_ = 0; 604 text_store_acp_sink_mask_ = 0;
287 return S_OK; 605 return S_OK;
288 } 606 }
289 607
290 STDMETHODIMP TsfTextStore::OnStartComposition( 608 STDMETHODIMP TsfTextStore::OnStartComposition(
291 ITfCompositionView* composition_view, 609 ITfCompositionView* composition_view,
292 BOOL* ok) { 610 BOOL* ok) {
293 if (!ok) 611 if (ok)
294 *ok = TRUE; 612 *ok = TRUE;
295 return S_OK; 613 return S_OK;
296 } 614 }
297 615
298 STDMETHODIMP TsfTextStore::OnUpdateComposition( 616 STDMETHODIMP TsfTextStore::OnUpdateComposition(
299 ITfCompositionView* composition_view, 617 ITfCompositionView* composition_view,
300 ITfRange* range) { 618 ITfRange* range) {
301 return S_OK; 619 return S_OK;
302 } 620 }
303 621
304 STDMETHODIMP TsfTextStore::OnEndComposition( 622 STDMETHODIMP TsfTextStore::OnEndComposition(
305 ITfCompositionView* composition_view) { 623 ITfCompositionView* composition_view) {
306 return S_OK; 624 return S_OK;
307 } 625 }
308 626
309 STDMETHODIMP TsfTextStore::OnEndEdit(ITfContext* context, 627 STDMETHODIMP TsfTextStore::OnEndEdit(ITfContext* context,
310 TfEditCookie read_only_edit_cookie, 628 TfEditCookie read_only_edit_cookie,
311 ITfEditRecord* edit_record) { 629 ITfEditRecord* edit_record) {
630 if (!context || !edit_record)
631 return E_INVALIDARG;
632
633 size_t committed_size;
634 CompositionUnderlines undelines;
635 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size,
636 &undelines)) {
637 return S_OK;
638 }
639 composition_undelines_ = undelines;
640 committed_size_ = committed_size;
641 edit_flag_ = true;
312 return S_OK; 642 return S_OK;
313 } 643 }
314 644
315 } // namespace ui 645 bool TsfTextStore::GetDisplayAttribute(TfGuidAtom guid_atom,
646 TF_DISPLAYATTRIBUTE* attribute) {
647 GUID guid;
648 if (FAILED(category_manager_->GetGUID(guid_atom, &guid)))
649 return false;
650
651 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info;
652 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo(
653 guid, display_attribute_info.Receive(), NULL))) {
654 return false;
655 }
656 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute));
657 }
658
659 bool TsfTextStore::GetCompositionStatus(
660 ITfContext* context,
661 const TfEditCookie read_only_edit_cookie,
662 size_t* committed_size,
663 CompositionUnderlines* undelines) {
664 DCHECK(context);
665 DCHECK(committed_size);
666 DCHECK(undelines);
667 const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE};
668 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property;
669 if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0,
670 track_property.Receive()))) {
671 return false;
672 }
673
674 *committed_size = 0;
675 undelines->clear();
676 base::win::ScopedComPtr<ITfRange> start_to_end_range;
677 base::win::ScopedComPtr<ITfRange> end_range;
678 if (FAILED(context->GetStart(read_only_edit_cookie,
679 start_to_end_range.Receive()))) {
680 return false;
681 }
682 if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.Receive())))
683 return false;
684 if (FAILED(start_to_end_range->ShiftEndToRange(read_only_edit_cookie,
685 end_range, TF_ANCHOR_END))) {
686 return false;
687 }
688
689 base::win::ScopedComPtr<IEnumTfRanges> ranges;
690 if (FAILED(track_property->EnumRanges(read_only_edit_cookie, ranges.Receive(),
691 start_to_end_range))) {
692 return false;
693 }
694
695 while (true) {
696 base::win::ScopedComPtr<ITfRange> range;
697 if (ranges->Next(1, range.Receive(), NULL) != S_OK)
698 break;
699 base::win::ScopedVariant value;
700 base::win::ScopedComPtr<IEnumTfPropertyValue> enum_prop_value;
701 if (FAILED(track_property->GetValue(read_only_edit_cookie, range,
702 value.Receive()))) {
703 return false;
704 }
705 if (FAILED(enum_prop_value.QueryFrom(value.AsInput()->punkVal)))
706 return false;
707
708 TF_PROPERTYVAL property_value;
709 bool is_composition = false;
710 bool has_display_attribute = false;
711 TF_DISPLAYATTRIBUTE display_attribute;
712 while (enum_prop_value->Next(1, &property_value, NULL) == S_OK) {
713 if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) {
714 is_composition = (property_value.varValue.lVal == TRUE);
715 } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) {
716 TfGuidAtom guid_atom =
717 static_cast<TfGuidAtom>(property_value.varValue.lVal);
718 if (GetDisplayAttribute(guid_atom, &display_attribute))
719 has_display_attribute = true;
720 }
721 VariantClear(&property_value.varValue);
722 }
723
724 base::win::ScopedComPtr<ITfRangeACP> range_acp;
725 range_acp.QueryFrom(range);
726 LONG start_pos, length;
727 range_acp->GetExtent(&start_pos, &length);
728 if (!is_composition) {
729 if (*committed_size < static_cast<size_t>(start_pos + length))
730 *committed_size = start_pos + length;
731 } else {
732 CompositionUnderline underline;
733 underline.start_offset = start_pos;
734 underline.end_offset = start_pos + length;
735 underline.color = SK_ColorBLACK;
736 if (has_display_attribute)
737 underline.thick = !!display_attribute.fBoldLine;
738 undelines->push_back(underline);
739 }
740 }
741 return true;
742 }
743
744 void TsfTextStore::SetFocusedTextInputClient(
745 HWND focused_window,
746 TextInputClient* text_input_client) {
747 window_handle_ = focused_window;
748 text_input_client_ = text_input_client;
749 }
750
751 void TsfTextStore::RemoveFocusedTextInputClient(
752 TextInputClient* text_input_client) {
753 if (text_input_client_ == text_input_client) {
754 window_handle_ = NULL;
755 text_input_client_ = NULL;
756 }
757 }
758
759 void TsfTextStore::SendOnLayoutChange() {
760 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE))
761 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
762 }
763
764 bool TsfTextStore::HasReadLock() const {
765 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ;
766 }
767
768 bool TsfTextStore::HasReadWriteLock() const {
769 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE;
770 }
771
772 } // namespace ui
OLDNEW
« no previous file with comments | « ui/base/win/tsf_text_store.h ('k') | ui/base/win/tsf_text_store_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698