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

Side by Side Diff: views/controls/textfield/native_textfield_win.cc

Issue 2741007: Fix unicode bidi mirroring characters are not correctly mirrored in textfield (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/controls/textfield/native_textfield_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 "views/controls/textfield/native_textfield_win.h" 5 #include "views/controls/textfield/native_textfield_win.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "app/clipboard/clipboard.h" 9 #include "app/clipboard/clipboard.h"
10 #include "app/clipboard/scoped_clipboard_writer.h" 10 #include "app/clipboard/scoped_clipboard_writer.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 if (count == 0) { 52 if (count == 0) {
53 // We need to UpdateWindow() here instead of InvalidateRect() because, as 53 // We need to UpdateWindow() here instead of InvalidateRect() because, as
54 // far as I can tell, the edit likes to synchronously erase its background 54 // far as I can tell, the edit likes to synchronously erase its background
55 // when unfreezing, thus requiring us to synchronously redraw if we don't 55 // when unfreezing, thus requiring us to synchronously redraw if we don't
56 // want flicker. 56 // want flicker.
57 edit_->UpdateWindow(); 57 edit_->UpdateWindow();
58 } 58 }
59 } 59 }
60 } 60 }
61 61
62 NativeTextfieldWin::ScopedSuspendUndo::ScopedSuspendUndo(
63 ITextDocument* text_object_model)
64 : text_object_model_(text_object_model) {
65 // Suspend Undo processing.
66 if (text_object_model_)
67 text_object_model_->Undo(tomSuspend, NULL);
68 }
69
70 NativeTextfieldWin::ScopedSuspendUndo::~ScopedSuspendUndo() {
71 // Resume Undo processing.
72 if (text_object_model_)
73 text_object_model_->Undo(tomResume, NULL);
74 }
75
62 /////////////////////////////////////////////////////////////////////////////// 76 ///////////////////////////////////////////////////////////////////////////////
63 // NativeTextfieldWin 77 // NativeTextfieldWin
64 78
65 bool NativeTextfieldWin::did_load_library_ = false; 79 bool NativeTextfieldWin::did_load_library_ = false;
66 80
67 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield) 81 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield)
68 : textfield_(textfield), 82 : textfield_(textfield),
69 tracking_double_click_(false), 83 tracking_double_click_(false),
70 double_click_time_(0), 84 double_click_time_(0),
71 can_discard_mousemove_(false), 85 can_discard_mousemove_(false),
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 ScopedFreeze freeze(this, GetTextObjectModel()); 321 ScopedFreeze freeze(this, GetTextObjectModel());
308 OnBeforePossibleChange(); 322 OnBeforePossibleChange();
309 switch (command_id) { 323 switch (command_id) {
310 case IDS_APP_UNDO: Undo(); break; 324 case IDS_APP_UNDO: Undo(); break;
311 case IDS_APP_CUT: Cut(); break; 325 case IDS_APP_CUT: Cut(); break;
312 case IDS_APP_COPY: Copy(); break; 326 case IDS_APP_COPY: Copy(); break;
313 case IDS_APP_PASTE: Paste(); break; 327 case IDS_APP_PASTE: Paste(); break;
314 case IDS_APP_SELECT_ALL: SelectAll(); break; 328 case IDS_APP_SELECT_ALL: SelectAll(); break;
315 default: NOTREACHED(); break; 329 default: NOTREACHED(); break;
316 } 330 }
317 OnAfterPossibleChange(); 331 OnAfterPossibleChange(true);
318 } 332 }
319 333
320 //////////////////////////////////////////////////////////////////////////////// 334 ////////////////////////////////////////////////////////////////////////////////
321 // NativeTextfieldWin, private: 335 // NativeTextfieldWin, private:
322 336
323 void NativeTextfieldWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { 337 void NativeTextfieldWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
324 HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags); 338 HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
325 } 339 }
326 340
327 void NativeTextfieldWin::OnContextMenu(HWND window, const POINT& point) { 341 void NativeTextfieldWin::OnContextMenu(HWND window, const POINT& point) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 422
409 const int composition_size = 423 const int composition_size =
410 ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0); 424 ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0);
411 if (composition_size >= 0) 425 if (composition_size >= 0)
412 ime_composition_length_ = composition_size / sizeof(wchar_t); 426 ime_composition_length_ = composition_size / sizeof(wchar_t);
413 427
414 ImmReleaseContext(m_hWnd, imm_context); 428 ImmReleaseContext(m_hWnd, imm_context);
415 } 429 }
416 } 430 }
417 431
418 OnAfterPossibleChange(); 432 // If we allow OnAfterPossibleChange() to redraw the text, it will do this by
433 // setting the edit's text directly, which can cancel the current IME
434 // composition or cause other adverse affects. So we set |should_redraw_text|
435 // to false.
436 OnAfterPossibleChange(false);
419 return result; 437 return result;
420 } 438 }
421 439
422 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message, 440 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message,
423 WPARAM wparam, 441 WPARAM wparam,
424 LPARAM lparam) { 442 LPARAM lparam) {
425 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without 443 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without
426 // sending any WM_IME_COMPOSITION messages when a user deletes all 444 // sending any WM_IME_COMPOSITION messages when a user deletes all
427 // composition characters, i.e. a composition string becomes empty. To handle 445 // composition characters, i.e. a composition string becomes empty. To handle
428 // this case, we need to update the find results when a composition is 446 // this case, we need to update the find results when a composition is
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 484
467 case VK_DELETE: 485 case VK_DELETE:
468 case 'X': 486 case 'X':
469 if ((flags & KF_ALTDOWN) || 487 if ((flags & KF_ALTDOWN) ||
470 (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0)) 488 (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0))
471 break; 489 break;
472 if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) { 490 if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) {
473 ScopedFreeze freeze(this, GetTextObjectModel()); 491 ScopedFreeze freeze(this, GetTextObjectModel());
474 OnBeforePossibleChange(); 492 OnBeforePossibleChange();
475 Cut(); 493 Cut();
476 OnAfterPossibleChange(); 494 OnAfterPossibleChange(true);
477 } 495 }
478 return; 496 return;
479 497
480 case 'C': 498 case 'C':
481 if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0)) 499 if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
482 break; 500 break;
483 if (GetKeyState(VK_SHIFT) >= 0) 501 if (GetKeyState(VK_SHIFT) >= 0)
484 Copy(); 502 Copy();
485 return; 503 return;
486 504
487 case VK_INSERT: 505 case VK_INSERT:
488 // Ignore insert by itself, so we don't turn overtype mode on/off. 506 // Ignore insert by itself, so we don't turn overtype mode on/off.
489 if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) && 507 if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) &&
490 (GetKeyState(VK_CONTROL) >= 0)) 508 (GetKeyState(VK_CONTROL) >= 0))
491 return; 509 return;
492 case 'V': 510 case 'V':
493 if ((flags & KF_ALTDOWN) || 511 if ((flags & KF_ALTDOWN) ||
494 (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0)) 512 (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
495 break; 513 break;
496 if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) { 514 if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
497 ScopedFreeze freeze(this, GetTextObjectModel()); 515 ScopedFreeze freeze(this, GetTextObjectModel());
498 OnBeforePossibleChange(); 516 OnBeforePossibleChange();
499 Paste(); 517 Paste();
500 OnAfterPossibleChange(); 518 OnAfterPossibleChange(true);
501 } 519 }
502 return; 520 return;
503 521
504 case 0xbb: // Ctrl-'='. Triggers subscripting, even in plain text mode. 522 case 0xbb: // Ctrl-'='. Triggers subscripting, even in plain text mode.
505 // We don't use VK_OEM_PLUS in case the macro isn't defined. 523 // We don't use VK_OEM_PLUS in case the macro isn't defined.
506 // (e.g., we don't have this symbol in embeded environment). 524 // (e.g., we don't have this symbol in embeded environment).
507 return; 525 return;
508 526
509 case VK_PROCESSKEY: 527 case VK_PROCESSKEY:
510 // This key event is consumed by an IME. 528 // This key event is consumed by an IME.
(...skipping 10 matching lines...) Expand all
521 void NativeTextfieldWin::OnLButtonDblClk(UINT keys, const CPoint& point) { 539 void NativeTextfieldWin::OnLButtonDblClk(UINT keys, const CPoint& point) {
522 // Save the double click info for later triple-click detection. 540 // Save the double click info for later triple-click detection.
523 tracking_double_click_ = true; 541 tracking_double_click_ = true;
524 double_click_point_ = point; 542 double_click_point_ = point;
525 double_click_time_ = GetCurrentMessage()->time; 543 double_click_time_ = GetCurrentMessage()->time;
526 544
527 ScopedFreeze freeze(this, GetTextObjectModel()); 545 ScopedFreeze freeze(this, GetTextObjectModel());
528 OnBeforePossibleChange(); 546 OnBeforePossibleChange();
529 DefWindowProc(WM_LBUTTONDBLCLK, keys, 547 DefWindowProc(WM_LBUTTONDBLCLK, keys,
530 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); 548 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
531 OnAfterPossibleChange(); 549 OnAfterPossibleChange(true);
532 } 550 }
533 551
534 void NativeTextfieldWin::OnLButtonDown(UINT keys, const CPoint& point) { 552 void NativeTextfieldWin::OnLButtonDown(UINT keys, const CPoint& point) {
535 // Check for triple click, then reset tracker. Should be safe to subtract 553 // Check for triple click, then reset tracker. Should be safe to subtract
536 // double_click_time_ from the current message's time even if the timer has 554 // double_click_time_ from the current message's time even if the timer has
537 // wrapped in between. 555 // wrapped in between.
538 const bool is_triple_click = tracking_double_click_ && 556 const bool is_triple_click = tracking_double_click_ &&
539 win_util::IsDoubleClick(double_click_point_, point, 557 win_util::IsDoubleClick(double_click_point_, point,
540 GetCurrentMessage()->time - double_click_time_); 558 GetCurrentMessage()->time - double_click_time_);
541 tracking_double_click_ = false; 559 tracking_double_click_ = false;
542 560
543 ScopedFreeze freeze(this, GetTextObjectModel()); 561 ScopedFreeze freeze(this, GetTextObjectModel());
544 OnBeforePossibleChange(); 562 OnBeforePossibleChange();
545 DefWindowProc(WM_LBUTTONDOWN, keys, 563 DefWindowProc(WM_LBUTTONDOWN, keys,
546 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click), 564 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
547 point.y)); 565 point.y));
548 OnAfterPossibleChange(); 566 OnAfterPossibleChange(true);
549 } 567 }
550 568
551 void NativeTextfieldWin::OnLButtonUp(UINT keys, const CPoint& point) { 569 void NativeTextfieldWin::OnLButtonUp(UINT keys, const CPoint& point) {
552 ScopedFreeze freeze(this, GetTextObjectModel()); 570 ScopedFreeze freeze(this, GetTextObjectModel());
553 OnBeforePossibleChange(); 571 OnBeforePossibleChange();
554 DefWindowProc(WM_LBUTTONUP, keys, 572 DefWindowProc(WM_LBUTTONUP, keys,
555 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); 573 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
556 OnAfterPossibleChange(); 574 OnAfterPossibleChange(true);
557 } 575 }
558 576
559 void NativeTextfieldWin::OnMouseLeave() { 577 void NativeTextfieldWin::OnMouseLeave() {
560 SetContainsMouse(false); 578 SetContainsMouse(false);
561 } 579 }
562 580
563 LRESULT NativeTextfieldWin::OnMouseWheel(UINT message, WPARAM w_param, 581 LRESULT NativeTextfieldWin::OnMouseWheel(UINT message, WPARAM w_param,
564 LPARAM l_param) { 582 LPARAM l_param) {
565 // Reroute the mouse-wheel to the window under the mouse pointer if 583 // Reroute the mouse-wheel to the window under the mouse pointer if
566 // applicable. 584 // applicable.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
612 // behaves strangely when the cursor is dragged vertically: if the cursor 630 // behaves strangely when the cursor is dragged vertically: if the cursor
613 // is in the middle of the text, drags inside the clip rect do nothing, 631 // is in the middle of the text, drags inside the clip rect do nothing,
614 // and drags outside the clip rect act as if the cursor jumped to the 632 // and drags outside the clip rect act as if the cursor jumped to the
615 // left edge of the text. When the cursor is at the right edge, drags of 633 // left edge of the text. When the cursor is at the right edge, drags of
616 // just a few pixels vertically end up selecting the "phantom newline"... 634 // just a few pixels vertically end up selecting the "phantom newline"...
617 // sometimes. 635 // sometimes.
618 RECT r; 636 RECT r;
619 GetRect(&r); 637 GetRect(&r);
620 DefWindowProc(WM_MOUSEMOVE, keys, 638 DefWindowProc(WM_MOUSEMOVE, keys,
621 MAKELPARAM(point.x, (r.bottom - r.top) / 2)); 639 MAKELPARAM(point.x, (r.bottom - r.top) / 2));
622 OnAfterPossibleChange(); 640 OnAfterPossibleChange(true);
623 } 641 }
624 } 642 }
625 643
626 int NativeTextfieldWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) { 644 int NativeTextfieldWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
627 content_insets_.Set(0, 0, 0, 0); 645 content_insets_.Set(0, 0, 0, 0);
628 if (textfield_->draw_border()) 646 if (textfield_->draw_border())
629 content_insets_ = CalculateInsets(); 647 content_insets_ = CalculateInsets();
630 if (w_param) { 648 if (w_param) {
631 NCCALCSIZE_PARAMS* nc_params = 649 NCCALCSIZE_PARAMS* nc_params =
632 reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param); 650 reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
789 // layout of the first LTR character in input text. Such keyboard layout 807 // layout of the first LTR character in input text. Such keyboard layout
790 // change behavior is surprising and inconsistent with keyboard behavior 808 // change behavior is surprising and inconsistent with keyboard behavior
791 // elsewhere, so reset the layout in this case. 809 // elsewhere, so reset the layout in this case.
792 HKL layout = GetKeyboardLayout(0); 810 HKL layout = GetKeyboardLayout(0);
793 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); 811 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
794 ActivateKeyboardLayout(layout, KLF_REORDER); 812 ActivateKeyboardLayout(layout, KLF_REORDER);
795 } else { 813 } else {
796 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); 814 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
797 } 815 }
798 816
799 OnAfterPossibleChange(); 817 // CRichEditCtrl automatically turns on IMF_AUTOKEYBOARD when the user
818 // inputs an RTL character, making it difficult for the user to control
819 // what language is set as they type. Force this off to make the edit's
820 // behavior more stable.
821 const int lang_options = SendMessage(EM_GETLANGOPTIONS, 0, 0);
822 if (lang_options & IMF_AUTOKEYBOARD)
823 SendMessage(EM_SETLANGOPTIONS, 0, lang_options & ~IMF_AUTOKEYBOARD);
824
825 OnAfterPossibleChange(true);
800 } 826 }
801 } 827 }
802 828
803 void NativeTextfieldWin::OnBeforePossibleChange() { 829 void NativeTextfieldWin::OnBeforePossibleChange() {
804 // Record our state. 830 // Record our state.
805 text_before_change_ = GetText(); 831 text_before_change_ = GetText();
806 } 832 }
807 833
808 void NativeTextfieldWin::OnAfterPossibleChange() { 834 void NativeTextfieldWin::OnAfterPossibleChange(bool should_redraw_text) {
809 // Prevent the user from selecting the "phantom newline" at the end of the 835 // Prevent the user from selecting the "phantom newline" at the end of the
810 // edit. If they try, we just silently move the end of the selection back to 836 // edit. If they try, we just silently move the end of the selection back to
811 // the end of the real text. 837 // the end of the real text.
812 CHARRANGE new_sel; 838 CHARRANGE new_sel;
813 GetSel(new_sel); 839 GetSel(new_sel);
814 const int length = GetTextLength(); 840 const int length = GetTextLength();
815 if (new_sel.cpMax > length) { 841 if (new_sel.cpMax > length) {
816 new_sel.cpMax = length; 842 new_sel.cpMax = length;
817 if (new_sel.cpMin > length) 843 if (new_sel.cpMin > length)
818 new_sel.cpMin = length; 844 new_sel.cpMin = length;
819 SetSel(new_sel); 845 SetSel(new_sel);
820 } 846 }
821 847
822 std::wstring new_text(GetText()); 848 std::wstring new_text(GetText());
823 if (new_text != text_before_change_) { 849 if (new_text != text_before_change_) {
824 if (ime_discard_composition_ && ime_composition_start_ >= 0 && 850 if (ime_discard_composition_ && ime_composition_start_ >= 0 &&
825 ime_composition_length_ > 0) { 851 ime_composition_length_ > 0) {
826 // A string retrieved with a GetText() call contains a string being 852 // A string retrieved with a GetText() call contains a string being
827 // composed by an IME. We remove the composition string from this search 853 // composed by an IME. We remove the composition string from this search
828 // string. 854 // string.
829 new_text.erase(ime_composition_start_, ime_composition_length_); 855 new_text.erase(ime_composition_start_, ime_composition_length_);
830 ime_composition_start_ = 0; 856 ime_composition_start_ = 0;
831 ime_composition_length_ = 0; 857 ime_composition_length_ = 0;
832 if (new_text.empty()) 858 if (new_text.empty())
833 return; 859 return;
834 } 860 }
835 textfield_->SyncText(); 861 textfield_->SyncText();
836 if (textfield_->GetController()) 862 if (textfield_->GetController())
837 textfield_->GetController()->ContentsChanged(textfield_, new_text); 863 textfield_->GetController()->ContentsChanged(textfield_, new_text);
864
865 if (should_redraw_text) {
866 CHARRANGE original_sel;
867 GetSel(original_sel);
868 std::wstring text = GetText();
869 ScopedSuspendUndo suspend_undo(GetTextObjectModel());
870
871 SelectAll();
872 ReplaceSel(reinterpret_cast<LPCTSTR>(text.c_str()), true);
873 SetSel(original_sel);
874 }
838 } 875 }
839 } 876 }
840 877
841 LONG NativeTextfieldWin::ClipXCoordToVisibleText(LONG x, 878 LONG NativeTextfieldWin::ClipXCoordToVisibleText(LONG x,
842 bool is_triple_click) const { 879 bool is_triple_click) const {
843 // Clip the X coordinate to the left edge of the text. Careful: 880 // Clip the X coordinate to the left edge of the text. Careful:
844 // PosFromChar(0) may return a negative X coordinate if the beginning of the 881 // PosFromChar(0) may return a negative X coordinate if the beginning of the
845 // text has scrolled off the edit, so don't go past the clip rect's edge. 882 // text has scrolled off the edit, so don't go past the clip rect's edge.
846 PARAFORMAT2 pf2; 883 PARAFORMAT2 pf2;
847 GetParaFormat(pf2); 884 GetParaFormat(pf2);
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
944 //////////////////////////////////////////////////////////////////////////////// 981 ////////////////////////////////////////////////////////////////////////////////
945 // NativeTextfieldWrapper, public: 982 // NativeTextfieldWrapper, public:
946 983
947 // static 984 // static
948 NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper( 985 NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
949 Textfield* field) { 986 Textfield* field) {
950 return new NativeTextfieldWin(field); 987 return new NativeTextfieldWin(field);
951 } 988 }
952 989
953 } // namespace views 990 } // namespace views
OLDNEW
« no previous file with comments | « views/controls/textfield/native_textfield_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698