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