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

Side by Side Diff: win8/metro_driver/ime/text_store.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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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 #define INITGUID // required for GUID_PROP_INPUTSCOPE 5 #define INITGUID // required for GUID_PROP_INPUTSCOPE
6 #include "ui/base/ime/win/tsf_text_store.h" 6 #include "win8/metro_driver/ime/text_store.h"
7 7
8 #include <InputScope.h> 8 #include <InputScope.h>
9 #include <OleCtl.h> 9 #include <OleCtl.h>
10 10
11 #include <algorithm> 11 #include <algorithm>
12 12
13 #include "base/win/scoped_variant.h" 13 #include "base/win/scoped_variant.h"
14 #include "ui/base/ime/text_input_client.h" 14 #include "win8/metro_driver/ime/input_scope.h"
15 #include "ui/base/ime/win/tsf_input_scope.h" 15 #include "win8/metro_driver/ime/text_store_delegate.h"
16 #include "ui/gfx/rect.h"
17 16
18 namespace ui { 17 namespace metro_driver {
19 namespace { 18 namespace {
20 19
21 // We support only one view. 20 // We support only one view.
22 const TsViewCookie kViewCookie = 1; 21 const TsViewCookie kViewCookie = 1;
23 22
24 } // namespace 23 } // namespace
25 24
26 TSFTextStore::TSFTextStore() 25 TSFTextStore::TSFTextStore(HWND window_handle,
26 const std::vector<InputScope>& input_scopes,
27 TextStoreDelegate* delegate)
27 : ref_count_(0), 28 : ref_count_(0),
28 text_store_acp_sink_mask_(0), 29 text_store_acp_sink_mask_(0),
29 window_handle_(NULL), 30 window_handle_(window_handle),
30 text_input_client_(NULL), 31 delegate_(delegate),
31 committed_size_(0), 32 committed_size_(0),
33 selection_start_(0),
34 selection_end_(0),
32 edit_flag_(false), 35 edit_flag_(false),
33 current_lock_type_(0) { 36 current_lock_type_(0) {
34 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) { 37 if (FAILED(category_manager_.CreateInstance(CLSID_TF_CategoryMgr))) {
35 LOG(FATAL) << "Failed to initialize CategoryMgr."; 38 LOG(FATAL) << "Failed to initialize CategoryMgr.";
36 return; 39 return;
37 } 40 }
38 if (FAILED(display_attribute_manager_.CreateInstance( 41 if (FAILED(display_attribute_manager_.CreateInstance(
39 CLSID_TF_DisplayAttributeMgr))) { 42 CLSID_TF_DisplayAttributeMgr))) {
40 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; 43 LOG(FATAL) << "Failed to initialize DisplayAttributeMgr.";
41 return; 44 return;
42 } 45 }
46 input_scope_ = CreteInputScope(input_scopes);
43 } 47 }
44 48
45 TSFTextStore::~TSFTextStore() { 49 TSFTextStore::~TSFTextStore() {
46 } 50 }
47 51
48 ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() { 52 ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() {
49 return InterlockedIncrement(&ref_count_); 53 return InterlockedIncrement(&ref_count_);
50 } 54 }
51 55
52 ULONG STDMETHODCALLTYPE TSFTextStore::Release() { 56 ULONG STDMETHODCALLTYPE TSFTextStore::Release() {
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 163
160 STDMETHODIMP TSFTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { 164 STDMETHODIMP TSFTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) {
161 if (view_cookie != kViewCookie) 165 if (view_cookie != kViewCookie)
162 return E_INVALIDARG; 166 return E_INVALIDARG;
163 if (!rect) 167 if (!rect)
164 return E_INVALIDARG; 168 return E_INVALIDARG;
165 169
166 // {0, 0, 0, 0} means that the document rect is not currently displayed. 170 // {0, 0, 0, 0} means that the document rect is not currently displayed.
167 SetRect(rect, 0, 0, 0, 0); 171 SetRect(rect, 0, 0, 0, 0);
168 172
169 if (!IsWindow(window_handle_))
170 return E_FAIL;
171
172 // Currently ui::TextInputClient does not expose the document rect. So use
173 // the Win32 client rectangle instead.
174 // TODO(yukawa): Upgrade TextInputClient so that the client can retrieve the
175 // document rectangle.
176 RECT client_rect = {}; 173 RECT client_rect = {};
177 if (!GetClientRect(window_handle_, &client_rect)) 174 if (!GetClientRect(window_handle_, &client_rect))
178 return E_FAIL; 175 return E_FAIL;
179 POINT left_top = {client_rect.left, client_rect.top}; 176 POINT left_top = {client_rect.left, client_rect.top};
180 POINT right_bottom = {client_rect.right, client_rect.bottom}; 177 POINT right_bottom = {client_rect.right, client_rect.bottom};
181 if (!ClientToScreen(window_handle_, &left_top)) 178 if (!ClientToScreen(window_handle_, &left_top))
182 return E_FAIL; 179 return E_FAIL;
183 if (!ClientToScreen(window_handle_, &right_bottom)) 180 if (!ClientToScreen(window_handle_, &right_bottom))
184 return E_FAIL; 181 return E_FAIL;
185 182
(...skipping 10 matching lines...) Expand all
196 ULONG* fetched_count) { 193 ULONG* fetched_count) {
197 if (!selection_buffer) 194 if (!selection_buffer)
198 return E_INVALIDARG; 195 return E_INVALIDARG;
199 if (!fetched_count) 196 if (!fetched_count)
200 return E_INVALIDARG; 197 return E_INVALIDARG;
201 if (!HasReadLock()) 198 if (!HasReadLock())
202 return TS_E_NOLOCK; 199 return TS_E_NOLOCK;
203 *fetched_count = 0; 200 *fetched_count = 0;
204 if ((selection_buffer_size > 0) && 201 if ((selection_buffer_size > 0) &&
205 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) { 202 ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) {
206 selection_buffer[0].acpStart = selection_.start(); 203 selection_buffer[0].acpStart = selection_start_;
207 selection_buffer[0].acpEnd = selection_.end(); 204 selection_buffer[0].acpEnd = selection_end_;
208 selection_buffer[0].style.ase = TS_AE_END; 205 selection_buffer[0].style.ase = TS_AE_END;
209 selection_buffer[0].style.fInterimChar = FALSE; 206 selection_buffer[0].style.fInterimChar = FALSE;
210 *fetched_count = 1; 207 *fetched_count = 1;
211 } 208 }
212 return S_OK; 209 return S_OK;
213 } 210 }
214 211
215 STDMETHODIMP TSFTextStore::GetStatus(TS_STATUS* status) { 212 STDMETHODIMP TSFTextStore::GetStatus(TS_STATUS* status) {
216 if (!status) 213 if (!status)
217 return E_INVALIDARG; 214 return E_INVALIDARG;
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 return S_OK; 266 return S_OK;
270 } 267 }
271 268
272 STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie, 269 STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie,
273 LONG acp_start, 270 LONG acp_start,
274 LONG acp_end, 271 LONG acp_end,
275 RECT* rect, 272 RECT* rect,
276 BOOL* clipped) { 273 BOOL* clipped) {
277 if (!rect || !clipped) 274 if (!rect || !clipped)
278 return E_INVALIDARG; 275 return E_INVALIDARG;
279 if (!text_input_client_) 276 if (!delegate_)
280 return E_UNEXPECTED; 277 return E_UNEXPECTED;
281 if (view_cookie != kViewCookie) 278 if (view_cookie != kViewCookie)
282 return E_INVALIDARG; 279 return E_INVALIDARG;
283 if (!HasReadLock()) 280 if (!HasReadLock())
284 return TS_E_NOLOCK; 281 return TS_E_NOLOCK;
285 if (!((static_cast<LONG>(committed_size_) <= acp_start) && 282 if (!((static_cast<LONG>(committed_size_) <= acp_start) &&
286 (acp_start <= acp_end) && 283 (acp_start <= acp_end) &&
287 (acp_end <= static_cast<LONG>(string_buffer_.size())))) { 284 (acp_end <= static_cast<LONG>(string_buffer_.size())))) {
288 return TS_E_INVALIDPOS; 285 return TS_E_INVALIDPOS;
289 } 286 }
290 287
291 // According to a behavior of notepad.exe and wordpad.exe, top left corner of 288 // According to a behavior of notepad.exe and wordpad.exe, top left corner of
292 // rect indicates a first character's one, and bottom right corner of rect 289 // rect indicates a first character's one, and bottom right corner of rect
293 // indicates a last character's one. 290 // indicates a last character's one.
294 // We use RECT instead of gfx::Rect since left position may be bigger than 291 // We use RECT instead of gfx::Rect since left position may be bigger than
295 // right position when composition has multiple lines. 292 // right position when composition has multiple lines.
296 RECT result; 293 RECT result;
297 gfx::Rect tmp_rect; 294 RECT tmp_rect;
298 const uint32 start_pos = acp_start - committed_size_; 295 const uint32 start_pos = acp_start - committed_size_;
299 const uint32 end_pos = acp_end - committed_size_; 296 const uint32 end_pos = acp_end - committed_size_;
300 297
301 if (start_pos == end_pos) { 298 if (start_pos == end_pos) {
302 // According to MSDN document, if |acp_start| and |acp_end| are equal it is 299 // According to MSDN document, if |acp_start| and |acp_end| are equal it is
303 // OK to just return E_INVALIDARG. 300 // OK to just return E_INVALIDARG.
304 // http://msdn.microsoft.com/en-us/library/ms538435 301 // http://msdn.microsoft.com/en-us/library/ms538435
305 // But when using Pinin IME of Windows 8, this method is called with the 302 // But when using Pinin IME of Windows 8, this method is called with the
306 // equal values of |acp_start| and |acp_end|. So we handle this condition. 303 // equal values of |acp_start| and |acp_end|. So we handle this condition.
307 if (start_pos == 0) { 304 if (start_pos == 0) {
308 if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) { 305 if (delegate_->GetCompositionCharacterBounds(0, &tmp_rect)) {
309 tmp_rect.set_width(0); 306 tmp_rect.right = tmp_rect.right;
310 result = tmp_rect.ToRECT(); 307 result = tmp_rect;
311 } else if (string_buffer_.size() == committed_size_) { 308 } else if (string_buffer_.size() == committed_size_) {
312 result = text_input_client_->GetCaretBounds().ToRECT(); 309 result = delegate_->GetCaretBounds();
313 } else { 310 } else {
314 return TS_E_NOLAYOUT; 311 return TS_E_NOLAYOUT;
315 } 312 }
316 } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1, 313 } else if (delegate_->GetCompositionCharacterBounds(start_pos - 1,
317 &tmp_rect)) { 314 &tmp_rect)) {
318 result.left = tmp_rect.right(); 315 tmp_rect.left = tmp_rect.right;
319 result.right = tmp_rect.right(); 316 result = tmp_rect;
320 result.top = tmp_rect.y();
321 result.bottom = tmp_rect.bottom();
322 } else { 317 } else {
323 return TS_E_NOLAYOUT; 318 return TS_E_NOLAYOUT;
324 } 319 }
325 } else { 320 } else {
326 if (text_input_client_->GetCompositionCharacterBounds(start_pos, 321 if (delegate_->GetCompositionCharacterBounds(start_pos, &tmp_rect)) {
327 &tmp_rect)) { 322 result = tmp_rect;
328 result.left = tmp_rect.x(); 323 if (delegate_->GetCompositionCharacterBounds(end_pos - 1, &tmp_rect)) {
329 result.top = tmp_rect.y(); 324 result.right = tmp_rect.right;
330 result.right = tmp_rect.right(); 325 result.bottom = tmp_rect.bottom;
331 result.bottom = tmp_rect.bottom();
332 if (text_input_client_->GetCompositionCharacterBounds(end_pos - 1,
333 &tmp_rect)) {
334 result.right = tmp_rect.right();
335 result.bottom = tmp_rect.bottom();
336 } else { 326 } else {
337 // We may not be able to get the last character bounds, so we use the 327 // We may not be able to get the last character bounds, so we use the
338 // first character bounds instead of returning TS_E_NOLAYOUT. 328 // first character bounds instead of returning TS_E_NOLAYOUT.
339 } 329 }
340 } else { 330 } else {
341 // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so 331 // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so
342 // it's better to return previous caret rectangle instead. 332 // it's better to return previous caret rectangle instead.
343 // TODO(nona, kinaba): Remove this hack. 333 // TODO(nona, kinaba): Remove this hack.
344 if (start_pos == 0) { 334 if (start_pos == 0) {
345 result = text_input_client_->GetCaretBounds().ToRECT(); 335 result = delegate_->GetCaretBounds();
346 } else { 336 } else {
347 return TS_E_NOLAYOUT; 337 return TS_E_NOLAYOUT;
348 } 338 }
349 } 339 }
350 } 340 }
351 341
352 *rect = result; 342 *rect = result;
353 *clipped = FALSE; 343 *clipped = FALSE;
354 return S_OK; 344 return S_OK;
355 } 345 }
(...skipping 27 matching lines...) Expand all
383 NOTIMPLEMENTED(); 373 NOTIMPLEMENTED();
384 return E_NOTIMPL; 374 return E_NOTIMPL;
385 } 375 }
386 376
387 STDMETHODIMP TSFTextStore::InsertTextAtSelection(DWORD flags, 377 STDMETHODIMP TSFTextStore::InsertTextAtSelection(DWORD flags,
388 const wchar_t* text_buffer, 378 const wchar_t* text_buffer,
389 ULONG text_buffer_size, 379 ULONG text_buffer_size,
390 LONG* acp_start, 380 LONG* acp_start,
391 LONG* acp_end, 381 LONG* acp_end,
392 TS_TEXTCHANGE* text_change) { 382 TS_TEXTCHANGE* text_change) {
393 const LONG start_pos = selection_.start(); 383 const LONG start_pos = selection_start_;
394 const LONG end_pos = selection_.end(); 384 const LONG end_pos = selection_end_;
395 const LONG new_end_pos = start_pos + text_buffer_size; 385 const LONG new_end_pos = start_pos + text_buffer_size;
396 386
397 if (flags & TS_IAS_QUERYONLY) { 387 if (flags & TS_IAS_QUERYONLY) {
398 if (!HasReadLock()) 388 if (!HasReadLock())
399 return TS_E_NOLOCK; 389 return TS_E_NOLOCK;
400 if (acp_start) 390 if (acp_start)
401 *acp_start = start_pos; 391 *acp_start = start_pos;
402 if (acp_end) { 392 if (acp_end) {
403 *acp_end = end_pos; 393 *acp_end = end_pos;
404 } 394 }
(...skipping 11 matching lines...) Expand all
416 string_buffer_.substr(end_pos); 406 string_buffer_.substr(end_pos);
417 if (acp_start) 407 if (acp_start)
418 *acp_start = start_pos; 408 *acp_start = start_pos;
419 if (acp_end) 409 if (acp_end)
420 *acp_end = new_end_pos; 410 *acp_end = new_end_pos;
421 if (text_change) { 411 if (text_change) {
422 text_change->acpStart = start_pos; 412 text_change->acpStart = start_pos;
423 text_change->acpOldEnd = end_pos; 413 text_change->acpOldEnd = end_pos;
424 text_change->acpNewEnd = new_end_pos; 414 text_change->acpNewEnd = new_end_pos;
425 } 415 }
426 selection_.set_start(start_pos); 416 selection_start_ = start_pos;
427 selection_.set_end(new_end_pos); 417 selection_end_ = new_end_pos;
428 return S_OK; 418 return S_OK;
429 } 419 }
430 420
431 STDMETHODIMP TSFTextStore::QueryInsert( 421 STDMETHODIMP TSFTextStore::QueryInsert(
432 LONG acp_test_start, 422 LONG acp_test_start,
433 LONG acp_test_end, 423 LONG acp_test_end,
434 ULONG text_size, 424 ULONG text_size,
435 LONG* acp_result_start, 425 LONG* acp_result_start,
436 LONG* acp_result_end) { 426 LONG* acp_result_end) {
437 if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end) 427 if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end)
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 current_lock_type_ = 0; 499 current_lock_type_ = 0;
510 500
511 // Handles the pending lock requests. 501 // Handles the pending lock requests.
512 while (!lock_queue_.empty()) { 502 while (!lock_queue_.empty()) {
513 current_lock_type_ = lock_queue_.front(); 503 current_lock_type_ = lock_queue_.front();
514 lock_queue_.pop_front(); 504 lock_queue_.pop_front();
515 text_store_acp_sink_->OnLockGranted(current_lock_type_); 505 text_store_acp_sink_->OnLockGranted(current_lock_type_);
516 current_lock_type_ = 0; 506 current_lock_type_ = 0;
517 } 507 }
518 508
519 if (!edit_flag_) { 509 if (!edit_flag_)
520 return S_OK; 510 return S_OK;
521 }
522 511
523 // If the text store is edited in OnLockGranted(), we may need to call 512 // If the text store is edited in OnLockGranted(), we may need to call
524 // TextInputClient::InsertText() or TextInputClient::SetCompositionText(). 513 // TextStoreDelegate::ConfirmComposition() or
514 // TextStoreDelegate::SetComposition().
525 const size_t new_committed_size = committed_size_; 515 const size_t new_committed_size = committed_size_;
526 const string16& new_committed_string = 516 const string16& new_committed_string =
527 string_buffer_.substr(last_committed_size, 517 string_buffer_.substr(last_committed_size,
528 new_committed_size - last_committed_size); 518 new_committed_size - last_committed_size);
529 const string16& composition_string = 519 const string16& composition_string =
530 string_buffer_.substr(new_committed_size); 520 string_buffer_.substr(new_committed_size);
531 521
532 // If there is new committed string, calls TextInputClient::InsertText(). 522 if (delegate_) {
533 if ((!new_committed_string.empty()) && text_input_client_) { 523 // If there is new committed string, calls
534 text_input_client_->InsertText(new_committed_string); 524 // TextStoreDelegate::ConfirmComposition().
525 if ((!new_committed_string.empty()))
526 delegate_->OnTextCommitted(new_committed_string);
527
528 // Calls TextInputClient::SetCompositionText().
529 std::vector<metro_viewer::UnderlineInfo> underlines = underlines_;
530 // Adjusts the offset.
531 for (size_t i = 0; i < underlines_.size(); ++i) {
532 underlines[i].start_offset -= new_committed_size;
533 underlines[i].end_offset -= new_committed_size;
534 }
535 int32 selection_start = 0;
536 int32 selection_end = 0;
537 if (selection_start_ >= new_committed_size)
538 selection_start = selection_start_ - new_committed_size;
539 if (selection_end_ >= new_committed_size)
540 selection_end = selection_end_ - new_committed_size;
541 delegate_->OnCompositionChanged(
542 composition_string, selection_start, selection_end, underlines);
535 } 543 }
536 544
537 // Calls TextInputClient::SetCompositionText().
538 CompositionText composition_text;
539 composition_text.text = composition_string;
540 composition_text.underlines = composition_undelines_;
541 // Adjusts the offset.
542 for (size_t i = 0; i < composition_text.underlines.size(); ++i) {
543 composition_text.underlines[i].start_offset -= new_committed_size;
544 composition_text.underlines[i].end_offset -= new_committed_size;
545 }
546 if (selection_.start() < new_committed_size) {
547 composition_text.selection.set_start(0);
548 } else {
549 composition_text.selection.set_start(
550 selection_.start() - new_committed_size);
551 }
552 if (selection_.end() < new_committed_size) {
553 composition_text.selection.set_end(0);
554 } else {
555 composition_text.selection.set_end(selection_.end() - new_committed_size);
556 }
557 if (text_input_client_)
558 text_input_client_->SetCompositionText(composition_text);
559
560 // If there is no composition string, clear the text store status. 545 // If there is no composition string, clear the text store status.
561 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). 546 // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange().
562 if ((composition_string.empty()) && (new_committed_size != 0)) { 547 if ((composition_string.empty()) && (new_committed_size != 0)) {
563 string_buffer_.clear(); 548 string_buffer_.clear();
564 committed_size_ = 0; 549 committed_size_ = 0;
565 selection_.set_start(0); 550 selection_start_ = 0;
566 selection_.set_end(0); 551 selection_end_ = 0;
567 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) 552 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
568 text_store_acp_sink_->OnSelectionChange(); 553 text_store_acp_sink_->OnSelectionChange();
569 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) 554 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
570 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); 555 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
571 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { 556 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
572 TS_TEXTCHANGE textChange; 557 TS_TEXTCHANGE textChange;
573 textChange.acpStart = 0; 558 textChange.acpStart = 0;
574 textChange.acpOldEnd = new_committed_size; 559 textChange.acpOldEnd = new_committed_size;
575 textChange.acpNewEnd = 0; 560 textChange.acpNewEnd = 0;
576 text_store_acp_sink_->OnTextChange(0, &textChange); 561 text_store_acp_sink_->OnTextChange(0, &textChange);
577 } 562 }
578 } 563 }
579 564
580 return S_OK; 565 return S_OK;
581 } 566 }
582 567
583 STDMETHODIMP TSFTextStore::RequestSupportedAttrs( 568 STDMETHODIMP TSFTextStore::RequestSupportedAttrs(
584 DWORD /* flags */, // Seems that we should ignore this. 569 DWORD /* flags */, // Seems that we should ignore this.
585 ULONG attribute_buffer_size, 570 ULONG attribute_buffer_size,
586 const TS_ATTRID* attribute_buffer) { 571 const TS_ATTRID* attribute_buffer) {
587 if (!attribute_buffer) 572 if (!attribute_buffer)
588 return E_INVALIDARG; 573 return E_INVALIDARG;
589 if (!text_input_client_) 574 if (!input_scope_)
590 return E_FAIL; 575 return E_FAIL;
591 // We support only input scope attribute. 576 // We support only input scope attribute.
592 for (size_t i = 0; i < attribute_buffer_size; ++i) { 577 for (size_t i = 0; i < attribute_buffer_size; ++i) {
593 if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i])) 578 if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i]))
594 return S_OK; 579 return S_OK;
595 } 580 }
596 return E_FAIL; 581 return E_FAIL;
597 } 582 }
598 583
599 STDMETHODIMP TSFTextStore::RetrieveRequestedAttrs( 584 STDMETHODIMP TSFTextStore::RetrieveRequestedAttrs(
600 ULONG attribute_buffer_size, 585 ULONG attribute_buffer_size,
601 TS_ATTRVAL* attribute_buffer, 586 TS_ATTRVAL* attribute_buffer,
602 ULONG* attribute_buffer_copied) { 587 ULONG* attribute_buffer_copied) {
603 if (!attribute_buffer_copied) 588 if (!attribute_buffer_copied)
604 return E_INVALIDARG; 589 return E_INVALIDARG;
590 *attribute_buffer_copied = 0;
605 if (!attribute_buffer) 591 if (!attribute_buffer)
606 return E_INVALIDARG; 592 return E_INVALIDARG;
607 if (!text_input_client_) 593 if (!input_scope_)
608 return E_UNEXPECTED; 594 return E_UNEXPECTED;
609 // We support only input scope attribute. 595 // We support only input scope attribute.
610 *attribute_buffer_copied = 0; 596 *attribute_buffer_copied = 0;
611 if (attribute_buffer_size == 0) 597 if (attribute_buffer_size == 0)
612 return S_OK; 598 return S_OK;
613 599
614 attribute_buffer[0].dwOverlapId = 0; 600 attribute_buffer[0].dwOverlapId = 0;
615 attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE; 601 attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE;
616 attribute_buffer[0].varValue.vt = VT_UNKNOWN; 602 attribute_buffer[0].varValue.vt = VT_UNKNOWN;
617 attribute_buffer[0].varValue.punkVal = tsf_inputscope::CreateInputScope( 603 attribute_buffer[0].varValue.punkVal = input_scope_;
618 text_input_client_->GetTextInputType(),
619 text_input_client_->GetTextInputMode());
620 attribute_buffer[0].varValue.punkVal->AddRef(); 604 attribute_buffer[0].varValue.punkVal->AddRef();
621 *attribute_buffer_copied = 1; 605 *attribute_buffer_copied = 1;
622 return S_OK; 606 return S_OK;
623 } 607 }
624 608
625 STDMETHODIMP TSFTextStore::SetSelection( 609 STDMETHODIMP TSFTextStore::SetSelection(
626 ULONG selection_buffer_size, 610 ULONG selection_buffer_size,
627 const TS_SELECTION_ACP* selection_buffer) { 611 const TS_SELECTION_ACP* selection_buffer) {
628 if (!HasReadWriteLock()) 612 if (!HasReadWriteLock())
629 return TF_E_NOLOCK; 613 return TF_E_NOLOCK;
630 if (selection_buffer_size > 0) { 614 if (selection_buffer_size > 0) {
631 const LONG start_pos = selection_buffer[0].acpStart; 615 const LONG start_pos = selection_buffer[0].acpStart;
632 const LONG end_pos = selection_buffer[0].acpEnd; 616 const LONG end_pos = selection_buffer[0].acpEnd;
633 if (!((static_cast<LONG>(committed_size_) <= start_pos) && 617 if (!((static_cast<LONG>(committed_size_) <= start_pos) &&
634 (start_pos <= end_pos) && 618 (start_pos <= end_pos) &&
635 (end_pos <= static_cast<LONG>(string_buffer_.size())))) { 619 (end_pos <= static_cast<LONG>(string_buffer_.size())))) {
636 return TF_E_INVALIDPOS; 620 return TF_E_INVALIDPOS;
637 } 621 }
638 selection_.set_start(start_pos); 622 selection_start_ = start_pos;
639 selection_.set_end(end_pos); 623 selection_end_ = end_pos;
640 } 624 }
641 return S_OK; 625 return S_OK;
642 } 626 }
643 627
644 STDMETHODIMP TSFTextStore::SetText(DWORD flags, 628 STDMETHODIMP TSFTextStore::SetText(DWORD flags,
645 LONG acp_start, 629 LONG acp_start,
646 LONG acp_end, 630 LONG acp_end,
647 const wchar_t* text_buffer, 631 const wchar_t* text_buffer,
648 ULONG text_buffer_size, 632 ULONG text_buffer_size,
649 TS_TEXTCHANGE* text_change) { 633 TS_TEXTCHANGE* text_change) {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
699 ITfRange* range) { 683 ITfRange* range) {
700 return S_OK; 684 return S_OK;
701 } 685 }
702 686
703 STDMETHODIMP TSFTextStore::OnEndComposition( 687 STDMETHODIMP TSFTextStore::OnEndComposition(
704 ITfCompositionView* composition_view) { 688 ITfCompositionView* composition_view) {
705 return S_OK; 689 return S_OK;
706 } 690 }
707 691
708 STDMETHODIMP TSFTextStore::OnEndEdit(ITfContext* context, 692 STDMETHODIMP TSFTextStore::OnEndEdit(ITfContext* context,
709 TfEditCookie read_only_edit_cookie, 693 TfEditCookie read_only_edit_cookie,
710 ITfEditRecord* edit_record) { 694 ITfEditRecord* edit_record) {
711 if (!context || !edit_record) 695 if (!context || !edit_record)
712 return E_INVALIDARG; 696 return E_INVALIDARG;
713 697 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size_,
714 size_t committed_size; 698 &underlines_)) {
715 CompositionUnderlines undelines;
716 if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size,
717 &undelines)) {
718 return S_OK; 699 return S_OK;
719 } 700 }
720 composition_undelines_ = undelines;
721 committed_size_ = committed_size;
722 edit_flag_ = true; 701 edit_flag_ = true;
723 return S_OK; 702 return S_OK;
724 } 703 }
725 704
726 bool TSFTextStore::GetDisplayAttribute(TfGuidAtom guid_atom, 705 bool TSFTextStore::GetDisplayAttribute(TfGuidAtom guid_atom,
727 TF_DISPLAYATTRIBUTE* attribute) { 706 TF_DISPLAYATTRIBUTE* attribute) {
728 GUID guid; 707 GUID guid;
729 if (FAILED(category_manager_->GetGUID(guid_atom, &guid))) 708 if (FAILED(category_manager_->GetGUID(guid_atom, &guid)))
730 return false; 709 return false;
731 710
732 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info; 711 base::win::ScopedComPtr<ITfDisplayAttributeInfo> display_attribute_info;
733 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo( 712 if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo(
734 guid, display_attribute_info.Receive(), NULL))) { 713 guid, display_attribute_info.Receive(), NULL))) {
735 return false; 714 return false;
736 } 715 }
737 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute)); 716 return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute));
738 } 717 }
739 718
740 bool TSFTextStore::GetCompositionStatus( 719 bool TSFTextStore::GetCompositionStatus(
741 ITfContext* context, 720 ITfContext* context,
742 const TfEditCookie read_only_edit_cookie, 721 const TfEditCookie read_only_edit_cookie,
743 size_t* committed_size, 722 size_t* committed_size,
744 CompositionUnderlines* undelines) { 723 std::vector<metro_viewer::UnderlineInfo>* undelines) {
745 DCHECK(context); 724 DCHECK(context);
746 DCHECK(committed_size); 725 DCHECK(committed_size);
747 DCHECK(undelines); 726 DCHECK(undelines);
748 const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE}; 727 const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE};
749 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property; 728 base::win::ScopedComPtr<ITfReadOnlyProperty> track_property;
750 if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0, 729 if (FAILED(context->TrackProperties(rgGuids, 2, NULL, 0,
751 track_property.Receive()))) { 730 track_property.Receive()))) {
752 return false; 731 return false;
753 } 732 }
754 733
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
803 } 782 }
804 783
805 base::win::ScopedComPtr<ITfRangeACP> range_acp; 784 base::win::ScopedComPtr<ITfRangeACP> range_acp;
806 range_acp.QueryFrom(range); 785 range_acp.QueryFrom(range);
807 LONG start_pos, length; 786 LONG start_pos, length;
808 range_acp->GetExtent(&start_pos, &length); 787 range_acp->GetExtent(&start_pos, &length);
809 if (!is_composition) { 788 if (!is_composition) {
810 if (*committed_size < static_cast<size_t>(start_pos + length)) 789 if (*committed_size < static_cast<size_t>(start_pos + length))
811 *committed_size = start_pos + length; 790 *committed_size = start_pos + length;
812 } else { 791 } else {
813 CompositionUnderline underline; 792 metro_viewer::UnderlineInfo underline;
814 underline.start_offset = start_pos; 793 underline.start_offset = start_pos;
815 underline.end_offset = start_pos + length; 794 underline.end_offset = start_pos + length;
816 underline.color = SK_ColorBLACK; 795 underline.thick = !!display_attribute.fBoldLine;
817 if (has_display_attribute)
818 underline.thick = !!display_attribute.fBoldLine;
819 undelines->push_back(underline); 796 undelines->push_back(underline);
820 } 797 }
821 } 798 }
822 return true; 799 return true;
823 } 800 }
824 801
825 void TSFTextStore::SetFocusedTextInputClient(
826 HWND focused_window,
827 TextInputClient* text_input_client) {
828 window_handle_ = focused_window;
829 text_input_client_ = text_input_client;
830 }
831
832 void TSFTextStore::RemoveFocusedTextInputClient(
833 TextInputClient* text_input_client) {
834 if (text_input_client_ == text_input_client) {
835 window_handle_ = NULL;
836 text_input_client_ = NULL;
837 }
838 }
839
840 bool TSFTextStore::CancelComposition() { 802 bool TSFTextStore::CancelComposition() {
841 // If there is an on-going document lock, we must not edit the text. 803 // If there is an on-going document lock, we must not edit the text.
842 if (edit_flag_) 804 if (edit_flag_)
843 return false; 805 return false;
844 806
845 if (string_buffer_.empty()) 807 if (string_buffer_.empty())
846 return true; 808 return true;
847 809
848 // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does 810 // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does
849 // not have a dedicated method to cancel composition. However, CUAS actually 811 // not have a dedicated method to cancel composition. However, CUAS actually
850 // has a protocol conversion from CPS_CANCEL into TSF operations. According 812 // has a protocol conversion from CPS_CANCEL into TSF operations. According
851 // to the observations on Windows 7, TIPs are expected to cancel composition 813 // to the observations on Windows 7, TIPs are expected to cancel composition
852 // when an on-going composition text is replaced with an empty string. So 814 // when an on-going composition text is replaced with an empty string. So
853 // we use the same operation to cancel composition here to minimize the risk 815 // we use the same operation to cancel composition here to minimize the risk
854 // of potential compatibility issues. 816 // of potential compatibility issues.
855 817
856 const size_t previous_buffer_size = string_buffer_.size(); 818 const size_t previous_buffer_size = string_buffer_.size();
857 string_buffer_.clear(); 819 string_buffer_.clear();
858 committed_size_ = 0; 820 committed_size_ = 0;
859 selection_.set_start(0); 821 selection_start_ = 0;
860 selection_.set_end(0); 822 selection_end_ = 0;
861 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) 823 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
862 text_store_acp_sink_->OnSelectionChange(); 824 text_store_acp_sink_->OnSelectionChange();
863 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) 825 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
864 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); 826 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
865 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { 827 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
866 TS_TEXTCHANGE textChange = {}; 828 TS_TEXTCHANGE textChange = {};
867 textChange.acpStart = 0; 829 textChange.acpStart = 0;
868 textChange.acpOldEnd = previous_buffer_size; 830 textChange.acpOldEnd = previous_buffer_size;
869 textChange.acpNewEnd = 0; 831 textChange.acpNewEnd = 0;
870 text_store_acp_sink_->OnTextChange(0, &textChange); 832 text_store_acp_sink_->OnTextChange(0, &textChange);
871 } 833 }
872 return true; 834 return true;
873 } 835 }
874 836
875 bool TSFTextStore::ConfirmComposition() { 837 bool TSFTextStore::ConfirmComposition() {
876 // If there is an on-going document lock, we must not edit the text. 838 // If there is an on-going document lock, we must not edit the text.
877 if (edit_flag_) 839 if (edit_flag_)
878 return false; 840 return false;
879 841
880 if (string_buffer_.empty()) 842 if (string_buffer_.empty())
881 return true; 843 return true;
882 844
883 // See the comment in TSFTextStore::CancelComposition. 845 // See the comment in TSFTextStore::CancelComposition.
884 // This logic is based on the observation about how to emulate 846 // This logic is based on the observation about how to emulate
885 // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS. 847 // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS.
886 848
887 const string16& composition_text = string_buffer_.substr(committed_size_); 849 const string16& composition_text = string_buffer_.substr(committed_size_);
888 if (!composition_text.empty()) 850 if (!composition_text.empty())
889 text_input_client_->InsertText(composition_text); 851 delegate_->OnTextCommitted(composition_text);
890 852
891 const size_t previous_buffer_size = string_buffer_.size(); 853 const size_t previous_buffer_size = string_buffer_.size();
892 string_buffer_.clear(); 854 string_buffer_.clear();
893 committed_size_ = 0; 855 committed_size_ = 0;
894 selection_.set_start(0); 856 selection_start_ = 0;
895 selection_.set_end(0); 857 selection_end_ = 0;
896 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) 858 if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE)
897 text_store_acp_sink_->OnSelectionChange(); 859 text_store_acp_sink_->OnSelectionChange();
898 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) 860 if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)
899 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); 861 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
900 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { 862 if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) {
901 TS_TEXTCHANGE textChange = {}; 863 TS_TEXTCHANGE textChange = {};
902 textChange.acpStart = 0; 864 textChange.acpStart = 0;
903 textChange.acpOldEnd = previous_buffer_size; 865 textChange.acpOldEnd = previous_buffer_size;
904 textChange.acpNewEnd = 0; 866 textChange.acpNewEnd = 0;
905 text_store_acp_sink_->OnTextChange(0, &textChange); 867 text_store_acp_sink_->OnTextChange(0, &textChange);
906 } 868 }
907 return true; 869 return true;
908 } 870 }
909 871
910 void TSFTextStore::SendOnLayoutChange() { 872 void TSFTextStore::SendOnLayoutChange() {
911 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) 873 if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE))
912 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); 874 text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0);
913 } 875 }
914 876
915 bool TSFTextStore::HasReadLock() const { 877 bool TSFTextStore::HasReadLock() const {
916 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ; 878 return (current_lock_type_ & TS_LF_READ) == TS_LF_READ;
917 } 879 }
918 880
919 bool TSFTextStore::HasReadWriteLock() const { 881 bool TSFTextStore::HasReadWriteLock() const {
920 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; 882 return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE;
921 } 883 }
922 884
923 } // namespace ui 885 } // namespace metro_driver
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698