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

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

Powered by Google App Engine
This is Rietveld 408576698