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

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

Powered by Google App Engine
This is Rietveld 408576698