| OLD | NEW |
| 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/views/controls/textfield/native_textfield_win.h" | 5 #include "ui/views/controls/textfield/native_textfield_win.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/i18n/case_conversion.h" | 9 #include "base/i18n/case_conversion.h" |
| 10 #include "base/i18n/rtl.h" | 10 #include "base/i18n/rtl.h" |
| 11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
| 13 #include "base/win/metro.h" | 13 #include "base/win/metro.h" |
| 14 #include "base/win/windows_version.h" | 14 #include "base/win/windows_version.h" |
| 15 #include "grit/ui_strings.h" | 15 #include "grit/ui_strings.h" |
| 16 #include "skia/ext/skia_utils_win.h" | 16 #include "skia/ext/skia_utils_win.h" |
| 17 #include "ui/base/accessibility/accessible_view_state.h" | 17 #include "ui/base/accessibility/accessible_view_state.h" |
| 18 #include "ui/base/clipboard/clipboard.h" | 18 #include "ui/base/clipboard/clipboard.h" |
| 19 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 19 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
| 20 #include "ui/base/events/event.h" | 20 #include "ui/base/events/event.h" |
| 21 #include "ui/base/keycodes/keyboard_codes.h" | 21 #include "ui/base/keycodes/keyboard_codes.h" |
| 22 #include "ui/base/ime/win/tsf_bridge.h" |
| 22 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" |
| 23 #include "ui/base/l10n/l10n_util_win.h" | 24 #include "ui/base/l10n/l10n_util_win.h" |
| 24 #include "ui/base/native_theme/native_theme_win.h" | 25 #include "ui/base/native_theme/native_theme_win.h" |
| 25 #include "ui/base/range/range.h" | 26 #include "ui/base/range/range.h" |
| 26 #include "ui/base/win/mouse_wheel_util.h" | 27 #include "ui/base/win/mouse_wheel_util.h" |
| 27 #include "ui/views/controls/label.h" | 28 #include "ui/views/controls/label.h" |
| 28 #include "ui/views/controls/menu/menu_item_view.h" | 29 #include "ui/views/controls/menu/menu_item_view.h" |
| 29 #include "ui/views/controls/menu/menu_model_adapter.h" | 30 #include "ui/views/controls/menu/menu_model_adapter.h" |
| 30 #include "ui/views/controls/menu/menu_runner.h" | 31 #include "ui/views/controls/menu/menu_runner.h" |
| 31 #include "ui/views/controls/native/native_view_host.h" | 32 #include "ui/views/controls/native/native_view_host.h" |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield) | 89 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield) |
| 89 : textfield_(textfield), | 90 : textfield_(textfield), |
| 90 tracking_double_click_(false), | 91 tracking_double_click_(false), |
| 91 double_click_time_(0), | 92 double_click_time_(0), |
| 92 can_discard_mousemove_(false), | 93 can_discard_mousemove_(false), |
| 93 contains_mouse_(false), | 94 contains_mouse_(false), |
| 94 ime_discard_composition_(false), | 95 ime_discard_composition_(false), |
| 95 ime_composition_start_(0), | 96 ime_composition_start_(0), |
| 96 ime_composition_length_(0), | 97 ime_composition_length_(0), |
| 97 container_view_(new NativeViewHost), | 98 container_view_(new NativeViewHost), |
| 98 bg_color_(0) { | 99 bg_color_(0), |
| 100 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 101 tsf_event_router_(base::win::IsTsfAwareRequired() ? |
| 102 new ui::TsfEventRouter(this) : NULL)) { |
| 99 if (!loaded_libarary_module_) { | 103 if (!loaded_libarary_module_) { |
| 100 // msftedit.dll is RichEdit ver 4.1. | 104 // msftedit.dll is RichEdit ver 4.1. |
| 101 // This version is available from WinXP SP1 and has TSF support. | 105 // This version is available from WinXP SP1 and has TSF support. |
| 102 loaded_libarary_module_ = LoadLibrary(L"msftedit.dll"); | 106 loaded_libarary_module_ = LoadLibrary(L"msftedit.dll"); |
| 103 } | 107 } |
| 104 | 108 |
| 105 DWORD style = kDefaultEditStyle | ES_AUTOHSCROLL; | 109 DWORD style = kDefaultEditStyle | ES_AUTOHSCROLL; |
| 106 if (textfield_->style() & Textfield::STYLE_OBSCURED) | 110 if (textfield_->style() & Textfield::STYLE_OBSCURED) |
| 107 style |= ES_PASSWORD; | 111 style |= ES_PASSWORD; |
| 108 | 112 |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 } | 341 } |
| 338 | 342 |
| 339 gfx::NativeView NativeTextfieldWin::GetTestingHandle() const { | 343 gfx::NativeView NativeTextfieldWin::GetTestingHandle() const { |
| 340 return m_hWnd; | 344 return m_hWnd; |
| 341 } | 345 } |
| 342 | 346 |
| 343 bool NativeTextfieldWin::IsIMEComposing() const { | 347 bool NativeTextfieldWin::IsIMEComposing() const { |
| 344 // Retrieve the length of the composition string to check if an IME is | 348 // Retrieve the length of the composition string to check if an IME is |
| 345 // composing text. (If this length is > 0 then an IME is being used to compose | 349 // composing text. (If this length is > 0 then an IME is being used to compose |
| 346 // text.) | 350 // text.) |
| 351 if (base::win::IsTsfAwareRequired()) |
| 352 return tsf_event_router_->IsImeComposing(); |
| 353 |
| 347 HIMC imm_context = ImmGetContext(m_hWnd); | 354 HIMC imm_context = ImmGetContext(m_hWnd); |
| 348 if (!imm_context) | 355 if (!imm_context) |
| 349 return false; | 356 return false; |
| 350 | 357 |
| 351 const int composition_size = ImmGetCompositionString(imm_context, GCS_COMPSTR, | 358 const int composition_size = ImmGetCompositionString(imm_context, GCS_COMPSTR, |
| 352 NULL, 0); | 359 NULL, 0); |
| 353 ImmReleaseContext(m_hWnd, imm_context); | 360 ImmReleaseContext(m_hWnd, imm_context); |
| 354 return composition_size > 0; | 361 return composition_size > 0; |
| 355 } | 362 } |
| 356 | 363 |
| 357 void NativeTextfieldWin::GetSelectedRange(ui::Range* range) const { | 364 void NativeTextfieldWin::GetSelectedRange(ui::Range* range) const { |
| 358 // TODO(tommi): Implement. | 365 // TODO(tommi): Implement. |
| 359 NOTIMPLEMENTED(); | 366 //NOTIMPLEMENTED(); |
| 360 range->set_start(0); | 367 range->set_start(0); |
| 361 range->set_end(0); | 368 range->set_end(0); |
| 362 } | 369 } |
| 363 | 370 |
| 364 void NativeTextfieldWin::SelectRange(const ui::Range& range) { | 371 void NativeTextfieldWin::SelectRange(const ui::Range& range) { |
| 365 // TODO(tommi): Implement. | 372 // TODO(tommi): Implement. |
| 366 NOTIMPLEMENTED(); | 373 NOTIMPLEMENTED(); |
| 367 } | 374 } |
| 368 | 375 |
| 369 void NativeTextfieldWin::GetSelectionModel(gfx::SelectionModel* sel) const { | 376 void NativeTextfieldWin::GetSelectionModel(gfx::SelectionModel* sel) const { |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 case IDS_APP_UNDO: Undo(); break; | 468 case IDS_APP_UNDO: Undo(); break; |
| 462 case IDS_APP_CUT: Cut(); break; | 469 case IDS_APP_CUT: Cut(); break; |
| 463 case IDS_APP_COPY: Copy(); break; | 470 case IDS_APP_COPY: Copy(); break; |
| 464 case IDS_APP_PASTE: Paste(); break; | 471 case IDS_APP_PASTE: Paste(); break; |
| 465 case IDS_APP_SELECT_ALL: SelectAll(false); break; | 472 case IDS_APP_SELECT_ALL: SelectAll(false); break; |
| 466 default: NOTREACHED(); break; | 473 default: NOTREACHED(); break; |
| 467 } | 474 } |
| 468 OnAfterPossibleChange(true); | 475 OnAfterPossibleChange(true); |
| 469 } | 476 } |
| 470 | 477 |
| 478 void NativeTextfieldWin::OnTextUpdated(const ui::Range& composition_range) { |
| 479 if (ime_discard_composition_) { |
| 480 ime_composition_start_ = composition_range.start(); |
| 481 ime_composition_length_ = composition_range.length(); |
| 482 } else { |
| 483 ime_composition_start_ = 0; |
| 484 ime_composition_length_ = 0; |
| 485 } |
| 486 OnAfterPossibleChange(false); |
| 487 text_before_change_.clear(); |
| 488 } |
| 489 |
| 490 void NativeTextfieldWin::OnCandidateWindowCountChanged(size_t window_count) { |
| 491 } |
| 492 |
| 493 void NativeTextfieldWin::OnImeStartCompositionInternal() { |
| 494 // Users may press alt+shift or control+shift keys to change their keyboard |
| 495 // layouts. So, we retrieve the input locale identifier everytime we start |
| 496 // an IME composition. |
| 497 int language_id = PRIMARYLANGID(GetKeyboardLayout(0)); |
| 498 ime_discard_composition_ = |
| 499 language_id == LANG_JAPANESE || language_id == LANG_CHINESE; |
| 500 ime_composition_start_ = 0; |
| 501 ime_composition_length_ = 0; |
| 502 } |
| 503 |
| 504 void NativeTextfieldWin::OnImeEndCompositionInternal() { |
| 505 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without |
| 506 // sending any WM_IME_COMPOSITION messages when a user deletes all |
| 507 // composition characters, i.e. a composition string becomes empty. To handle |
| 508 // this case, we need to update the find results when a composition is |
| 509 // finished or canceled. |
| 510 textfield_->SyncText(); |
| 511 } |
| 512 |
| 513 void NativeTextfieldWin::OnTsfStartComposition() { |
| 514 OnImeStartCompositionInternal(); |
| 515 } |
| 516 |
| 517 void NativeTextfieldWin::OnTsfEndComposition() { |
| 518 OnImeEndCompositionInternal(); |
| 519 } |
| 520 |
| 471 void NativeTextfieldWin::InitializeAccessibilityInfo() { | 521 void NativeTextfieldWin::InitializeAccessibilityInfo() { |
| 472 // Set the accessible state. | 522 // Set the accessible state. |
| 473 accessibility_state_ = 0; | 523 accessibility_state_ = 0; |
| 474 | 524 |
| 475 base::win::ScopedComPtr<IAccPropServices> pAccPropServices; | 525 base::win::ScopedComPtr<IAccPropServices> pAccPropServices; |
| 476 HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, | 526 HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, |
| 477 IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices)); | 527 IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices)); |
| 478 if (!SUCCEEDED(hr)) | 528 if (!SUCCEEDED(hr)) |
| 479 return; | 529 return; |
| 480 | 530 |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 ui::Clipboard::GetForCurrentThread(), | 619 ui::Clipboard::GetForCurrentThread(), |
| 570 ui::Clipboard::BUFFER_STANDARD); | 620 ui::Clipboard::BUFFER_STANDARD); |
| 571 scw.WriteText(text); | 621 scw.WriteText(text); |
| 572 } | 622 } |
| 573 } | 623 } |
| 574 | 624 |
| 575 LRESULT NativeTextfieldWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) { | 625 LRESULT NativeTextfieldWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) { |
| 576 if (base::win::IsTsfAwareRequired()) { | 626 if (base::win::IsTsfAwareRequired()) { |
| 577 // Enable TSF support of RichEdit. | 627 // Enable TSF support of RichEdit. |
| 578 SetEditStyle(SES_USECTF, SES_USECTF); | 628 SetEditStyle(SES_USECTF, SES_USECTF); |
| 629 |
| 630 // When TSF is enabled, OnTextUpdated() may be called without any previous |
| 631 // call that would have indicated the start of an editing session. In order |
| 632 // to guarantee we've always called OnBeforePossibleChange() before |
| 633 // OnAfterPossibleChange(), we therefore call that here. Note that multiple |
| 634 // (i.e. unmatched) calls to this function in a row are safe. |
| 635 OnBeforePossibleChange(); |
| 579 } | 636 } |
| 580 SetMsgHandled(FALSE); | 637 SetMsgHandled(FALSE); |
| 581 return 0; | 638 return 0; |
| 582 } | 639 } |
| 583 | 640 |
| 584 void NativeTextfieldWin::OnCut() { | 641 void NativeTextfieldWin::OnCut() { |
| 585 if (textfield_->read_only() || textfield_->IsObscured()) | 642 if (textfield_->read_only() || textfield_->IsObscured()) |
| 586 return; | 643 return; |
| 587 | 644 |
| 588 OnCopy(); | 645 OnCopy(); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 610 // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message. | 667 // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message. |
| 611 // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR | 668 // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR |
| 612 // messages from being dispatched to view controls via the CallWindowProc() | 669 // messages from being dispatched to view controls via the CallWindowProc() |
| 613 // call. | 670 // call. |
| 614 return 0; | 671 return 0; |
| 615 } | 672 } |
| 616 | 673 |
| 617 LRESULT NativeTextfieldWin::OnImeStartComposition(UINT message, | 674 LRESULT NativeTextfieldWin::OnImeStartComposition(UINT message, |
| 618 WPARAM wparam, | 675 WPARAM wparam, |
| 619 LPARAM lparam) { | 676 LPARAM lparam) { |
| 620 // Users may press alt+shift or control+shift keys to change their keyboard | 677 OnImeStartCompositionInternal(); |
| 621 // layouts. So, we retrieve the input locale identifier everytime we start | |
| 622 // an IME composition. | |
| 623 int language_id = PRIMARYLANGID(GetKeyboardLayout(0)); | |
| 624 ime_discard_composition_ = | |
| 625 language_id == LANG_JAPANESE || language_id == LANG_CHINESE; | |
| 626 ime_composition_start_ = 0; | |
| 627 ime_composition_length_ = 0; | |
| 628 | |
| 629 return DefWindowProc(message, wparam, lparam); | 678 return DefWindowProc(message, wparam, lparam); |
| 630 } | 679 } |
| 631 | 680 |
| 632 LRESULT NativeTextfieldWin::OnImeComposition(UINT message, | 681 LRESULT NativeTextfieldWin::OnImeComposition(UINT message, |
| 633 WPARAM wparam, | 682 WPARAM wparam, |
| 634 LPARAM lparam) { | 683 LPARAM lparam) { |
| 635 text_before_change_.clear(); | 684 text_before_change_.clear(); |
| 636 LRESULT result = DefWindowProc(message, wparam, lparam); | 685 LRESULT result = DefWindowProc(message, wparam, lparam); |
| 637 | 686 |
| 638 ime_composition_start_ = 0; | 687 ime_composition_start_ = 0; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 665 // setting the edit's text directly, which can cancel the current IME | 714 // setting the edit's text directly, which can cancel the current IME |
| 666 // composition or cause other adverse affects. So we set |should_redraw_text| | 715 // composition or cause other adverse affects. So we set |should_redraw_text| |
| 667 // to false. | 716 // to false. |
| 668 OnAfterPossibleChange(false); | 717 OnAfterPossibleChange(false); |
| 669 return result; | 718 return result; |
| 670 } | 719 } |
| 671 | 720 |
| 672 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message, | 721 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message, |
| 673 WPARAM wparam, | 722 WPARAM wparam, |
| 674 LPARAM lparam) { | 723 LPARAM lparam) { |
| 675 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without | 724 OnImeEndCompositionInternal(); |
| 676 // sending any WM_IME_COMPOSITION messages when a user deletes all | |
| 677 // composition characters, i.e. a composition string becomes empty. To handle | |
| 678 // this case, we need to update the find results when a composition is | |
| 679 // finished or canceled. | |
| 680 textfield_->SyncText(); | |
| 681 return DefWindowProc(message, wparam, lparam); | 725 return DefWindowProc(message, wparam, lparam); |
| 682 } | 726 } |
| 683 | 727 |
| 684 LRESULT NativeTextfieldWin::OnPointerDown(UINT message, WPARAM wparam, | 728 LRESULT NativeTextfieldWin::OnPointerDown(UINT message, WPARAM wparam, |
| 685 LPARAM lparam) { | 729 LPARAM lparam) { |
| 686 SetFocus(); | 730 SetFocus(); |
| 687 SetMsgHandled(FALSE); | 731 SetMsgHandled(FALSE); |
| 688 return 0; | 732 return 0; |
| 689 } | 733 } |
| 690 | 734 |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1004 | 1048 |
| 1005 void NativeTextfieldWin::OnSetFocus(HWND hwnd) { | 1049 void NativeTextfieldWin::OnSetFocus(HWND hwnd) { |
| 1006 SetMsgHandled(FALSE); // We still want the default processing of the message. | 1050 SetMsgHandled(FALSE); // We still want the default processing of the message. |
| 1007 | 1051 |
| 1008 views::FocusManager* focus_manager = textfield_->GetFocusManager(); | 1052 views::FocusManager* focus_manager = textfield_->GetFocusManager(); |
| 1009 if (!focus_manager) { | 1053 if (!focus_manager) { |
| 1010 NOTREACHED(); | 1054 NOTREACHED(); |
| 1011 return; | 1055 return; |
| 1012 } | 1056 } |
| 1013 focus_manager->SetFocusedView(textfield_); | 1057 focus_manager->SetFocusedView(textfield_); |
| 1058 |
| 1059 if (!base::win::IsTsfAwareRequired()) { |
| 1060 return; |
| 1061 } |
| 1062 |
| 1063 DefWindowProc(); |
| 1064 |
| 1065 // Document manager created by RichEdit can be obtained only after |
| 1066 // WM_SET_FOCUS event is handled. |
| 1067 tsf_event_router_->SetManager( |
| 1068 ui::TsfBridge::GetInstance()->GetThreadManager()); |
| 1069 SetMsgHandled(TRUE); |
| 1070 } |
| 1071 |
| 1072 void NativeTextfieldWin::OnKillFocus(HWND hwnd) { |
| 1073 if(tsf_event_router_) |
| 1074 tsf_event_router_->SetManager(NULL); |
| 1014 } | 1075 } |
| 1015 | 1076 |
| 1016 void NativeTextfieldWin::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { | 1077 void NativeTextfieldWin::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { |
| 1017 // Nearly all alt-<xxx> combos result in beeping rather than doing something | 1078 // Nearly all alt-<xxx> combos result in beeping rather than doing something |
| 1018 // useful, so we discard most. Exceptions: | 1079 // useful, so we discard most. Exceptions: |
| 1019 // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead | 1080 // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead |
| 1020 // of WM_SYSCHAR, so it doesn't need to be handled here. | 1081 // of WM_SYSCHAR, so it doesn't need to be handled here. |
| 1021 // * alt-space gets translated by the default WM_SYSCHAR handler to a | 1082 // * alt-space gets translated by the default WM_SYSCHAR handler to a |
| 1022 // WM_SYSCOMMAND to open the application context menu, so we need to allow | 1083 // WM_SYSCOMMAND to open the application context menu, so we need to allow |
| 1023 // it through. | 1084 // it through. |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1216 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); | 1277 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1217 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); | 1278 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
| 1218 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 1279 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
| 1219 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); | 1280 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
| 1220 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); | 1281 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1221 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, | 1282 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, |
| 1222 IDS_APP_SELECT_ALL); | 1283 IDS_APP_SELECT_ALL); |
| 1223 } | 1284 } |
| 1224 | 1285 |
| 1225 } // namespace views | 1286 } // namespace views |
| OLD | NEW |