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

Side by Side Diff: chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm

Issue 2426983002: Revert of [Mac] Preserve original selection when suggesting completions with diacritics (Closed)
Patch Set: Created 4 years, 2 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h" 5 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
6 6
7 #include "base/i18n/break_iterator.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/mac/sdk_forward_declarations.h" 7 #include "base/mac/sdk_forward_declarations.h"
11 #include "base/strings/string_util.h" 8 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h" 9 #include "base/strings/sys_string_conversions.h"
13 #include "chrome/app/chrome_command_ids.h" // IDC_* 10 #include "chrome/app/chrome_command_ids.h" // IDC_*
14 #include "chrome/browser/themes/theme_service.h" 11 #include "chrome/browser/themes/theme_service.h"
15 #include "chrome/browser/ui/browser_list.h" 12 #include "chrome/browser/ui/browser_list.h"
16 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 13 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
17 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" 14 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
18 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" 15 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
19 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 16 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
(...skipping 25 matching lines...) Expand all
45 BOOL ThePasteboardIsTooDamnBig() { 42 BOOL ThePasteboardIsTooDamnBig() {
46 NSPasteboard* pb = [NSPasteboard generalPasteboard]; 43 NSPasteboard* pb = [NSPasteboard generalPasteboard];
47 NSString* type = 44 NSString* type =
48 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; 45 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]];
49 if (!type) 46 if (!type)
50 return NO; 47 return NO;
51 48
52 return [[pb stringForType:type] length] > kMaxPasteLength; 49 return [[pb stringForType:type] length] > kMaxPasteLength;
53 } 50 }
54 51
55 NSRange VisualSelectedRangeFromRange(NSRange range, NSString* string) {
56 if (range.location >= string.length)
57 return range;
58 base::string16 text = base::SysNSStringToUTF16(string);
59 base::i18n::BreakIterator grapheme_iterator(
60 text, base::i18n::BreakIterator::BREAK_CHARACTER);
61 if (!grapheme_iterator.Init())
62 return range;
63 // This works because NSString uses UTF-16 code units.
64 while (range.location < text.length() &&
65 !grapheme_iterator.IsGraphemeBoundary(
66 static_cast<size_t>(range.location))) {
67 range.location++;
68 if (range.length > 0)
69 range.length--;
70 }
71 return range;
72 }
73
74 } // namespace 52 } // namespace
75 53
76 // Method exposed for the purpose of overriding.
77 // Used to restore model's selection range when the view doesn't
78 // match the model due to combining characters.
79 //
80 // In some cases, (completing 'y' to 'ÿour', for example), the autocomplete
81 // system requests a selection range that begins on a combining character.
82 // setSelectedRange: and friends document that the range passed to them
83 // "must begin and end on glyph boundaries and not split base glyphs and
84 // their nonspacing marks". If passed such a range, the selection is
85 // expanded to include the original user input, preventing the user
86 // from being able to type other words beginning with that letter.
87 //
88 // To resolve this, the field editor modifies the selection to start
89 // on the next glyph boundary, then keeps track of the original and
90 // modified selections, substituting the original when the user takes
91 // actions that operate on the selection. Since there are many methods
92 // in NSResponder (for example deleteToBeginningOfLine:) that operate
93 // on the selection, rather than shimming them all, we override this
94 // private method that they're implemented in terms of.
95 //
96 @interface NSTextView (PrivateTextEditing)
97 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string;
98 @end
99
100 @interface AutocompleteTextFieldEditor ()<NSDraggingSource> 54 @interface AutocompleteTextFieldEditor ()<NSDraggingSource>
101 @end 55 @end
102 56
103 @implementation AutocompleteTextFieldEditor 57 @implementation AutocompleteTextFieldEditor
104 58
105 @synthesize actualSelectedRange = actualSelectedRange_;
106
107 - (BOOL)shouldDrawInsertionPoint { 59 - (BOOL)shouldDrawInsertionPoint {
108 return [super shouldDrawInsertionPoint] && 60 return [super shouldDrawInsertionPoint] &&
109 ![[[self delegate] cell] hideFocusState]; 61 ![[[self delegate] cell] hideFocusState];
110 } 62 }
111 63
112 - (id)initWithFrame:(NSRect)frameRect { 64 - (id)initWithFrame:(NSRect)frameRect {
113 if ((self = [super initWithFrame:frameRect])) { 65 if ((self = [super initWithFrame:frameRect])) {
114 dropHandler_.reset([[URLDropTargetHandler alloc] initWithView:self]); 66 dropHandler_.reset([[URLDropTargetHandler alloc] initWithView:self]);
115 67
116 forbiddenCharacters_.reset([[NSCharacterSet controlCharacterSet] retain]); 68 forbiddenCharacters_.reset([[NSCharacterSet controlCharacterSet] retain]);
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 // (URLDropTarget protocol) 393 // (URLDropTarget protocol)
442 - (void)draggingExited:(id<NSDraggingInfo>)sender { 394 - (void)draggingExited:(id<NSDraggingInfo>)sender {
443 return [dropHandler_ draggingExited:sender]; 395 return [dropHandler_ draggingExited:sender];
444 } 396 }
445 397
446 // (URLDropTarget protocol) 398 // (URLDropTarget protocol)
447 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { 399 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
448 return [dropHandler_ performDragOperation:sender]; 400 return [dropHandler_ performDragOperation:sender];
449 } 401 }
450 402
451 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string {
452 if (NSEqualRanges(visualSelectedRange_, range) &&
453 !NSEqualRanges(visualSelectedRange_, actualSelectedRange_)) {
454 range = actualSelectedRange_;
455 }
456 [super _userReplaceRange:range withString:string];
457 }
458
459 // Prevent control characters from being entered into the Omnibox. 403 // Prevent control characters from being entered into the Omnibox.
460 // This is invoked for keyboard entry, not for pasting. 404 // This is invoked for keyboard entry, not for pasting.
461 - (void)insertText:(id)aString { 405 - (void)insertText:(id)aString {
462 AutocompleteTextFieldObserver* observer = [self observer]; 406 AutocompleteTextFieldObserver* observer = [self observer];
463 if (observer) 407 if (observer)
464 observer->OnInsertText(); 408 observer->OnInsertText();
465 409
466 // Repeatedly remove control characters. The loop will only ever 410 // Repeatedly remove control characters. The loop will only ever
467 // execute at all when the user enters control characters (using 411 // execute at all when the user enters control characters (using
468 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would 412 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would
(...skipping 11 matching lines...) Expand all
480 } else { 424 } else {
481 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 425 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
482 while (range.location != NSNotFound) { 426 while (range.location != NSNotFound) {
483 aString = 427 aString =
484 [aString stringByReplacingCharactersInRange:range withString:@""]; 428 [aString stringByReplacingCharactersInRange:range withString:@""];
485 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 429 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
486 } 430 }
487 DCHECK_EQ(range.length, 0U); 431 DCHECK_EQ(range.length, 0U);
488 } 432 }
489 433
490 if (!NSEqualRanges(visualSelectedRange_, actualSelectedRange_)) { 434 // NOTE: If |aString| is empty, this intentionally replaces the
491 [super replaceCharactersInRange:actualSelectedRange_ withString:aString]; 435 // selection with empty. This seems consistent with the case where
492 } else { 436 // the input contained a mixture of characters and the string ended
493 // NOTE: If |aString| is empty, this intentionally replaces the 437 // up not empty.
494 // selection with empty. This seems consistent with the case where 438 [super insertText:aString];
495 // the input contained a mixture of characters and the string ended
496 // up not empty.
497 [super insertText:aString];
498 }
499 } 439 }
500 440
501 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange 441 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
502 granularity:(NSSelectionGranularity)granularity { 442 granularity:(NSSelectionGranularity)granularity {
503 AutocompleteTextFieldObserver* observer = [self observer]; 443 AutocompleteTextFieldObserver* observer = [self observer];
504 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange 444 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange
505 granularity:granularity]; 445 granularity:granularity];
506 if (observer) 446 if (observer)
507 return observer->SelectionRangeForProposedRange(modifiedRange); 447 return observer->SelectionRangeForProposedRange(modifiedRange);
508 return modifiedRange; 448 return modifiedRange;
509 } 449 }
510 450
511 - (void)setSelectedRange:(NSRange)charRange 451 - (void)setSelectedRange:(NSRange)charRange
512 affinity:(NSSelectionAffinity)affinity 452 affinity:(NSSelectionAffinity)affinity
513 stillSelecting:(BOOL)flag { 453 stillSelecting:(BOOL)flag {
514 [super setSelectedRange:charRange affinity:affinity stillSelecting:flag]; 454 [super setSelectedRange:charRange affinity:affinity stillSelecting:flag];
515 455
516 // We're only interested in selection changes directly caused by keyboard 456 // We're only interested in selection changes directly caused by keyboard
517 // input from the user. 457 // input from the user.
518 if (interpretingKeyEvents_) 458 if (interpretingKeyEvents_)
519 textChangedByKeyEvents_ = YES; 459 textChangedByKeyEvents_ = YES;
520 } 460 }
521 461
522 - (void)setSelectedRanges:(NSArray*)ranges
523 affinity:(NSSelectionAffinity)affinity
524 stillSelecting:(BOOL)flag {
525 DCHECK(ranges.count > 0);
526 base::scoped_nsobject<NSMutableArray> mutableRanges([ranges mutableCopy]);
527 // |ranges| is sorted, and empirically, the first range passed is returned
528 // as selectedRange.
529 NSRange firstRange = [base::mac::ObjCCastStrict<NSValue>(
530 [mutableRanges firstObject]) rangeValue];
531 actualSelectedRange_ = firstRange;
532 visualSelectedRange_ =
533 VisualSelectedRangeFromRange(firstRange, [self string]);
534 NSValue* boxedVisualRange = [NSValue valueWithRange:visualSelectedRange_];
535 [mutableRanges replaceObjectAtIndex:0 withObject:boxedVisualRange];
536
537 [super setSelectedRanges:mutableRanges affinity:affinity stillSelecting:flag];
538 }
539
540 - (void)interpretKeyEvents:(NSArray *)eventArray { 462 - (void)interpretKeyEvents:(NSArray *)eventArray {
541 DCHECK(!interpretingKeyEvents_); 463 DCHECK(!interpretingKeyEvents_);
542 interpretingKeyEvents_ = YES; 464 interpretingKeyEvents_ = YES;
543 textChangedByKeyEvents_ = NO; 465 textChangedByKeyEvents_ = NO;
544 AutocompleteTextFieldObserver* observer = [self observer]; 466 AutocompleteTextFieldObserver* observer = [self observer];
545 467
546 if (observer) 468 if (observer)
547 observer->OnBeforeChange(); 469 observer->OnBeforeChange();
548 470
549 [super interpretKeyEvents:eventArray]; 471 [super interpretKeyEvents:eventArray];
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
635 [textStorage setAttributedString:aString]; 557 [textStorage setAttributedString:aString];
636 558
637 // The text has been changed programmatically. The observer should know 559 // The text has been changed programmatically. The observer should know
638 // this change, so setting |textChangedByKeyEvents_| to NO to 560 // this change, so setting |textChangedByKeyEvents_| to NO to
639 // prevent its OnDidChange() method from being called unnecessarily. 561 // prevent its OnDidChange() method from being called unnecessarily.
640 textChangedByKeyEvents_ = NO; 562 textChangedByKeyEvents_ = NO;
641 } 563 }
642 564
643 - (BOOL)validateMenuItem:(NSMenuItem*)item { 565 - (BOOL)validateMenuItem:(NSMenuItem*)item {
644 if ([item action] == @selector(copyToFindPboard:)) 566 if ([item action] == @selector(copyToFindPboard:))
645 return actualSelectedRange_.length > 0; 567 return [self selectedRange].length > 0;
646 if ([item action] == @selector(pasteAndGo:)) { 568 if ([item action] == @selector(pasteAndGo:)) {
647 // TODO(rohitrao): If the clipboard is empty, should we show a 569 // TODO(rohitrao): If the clipboard is empty, should we show a
648 // greyed-out "Paste and Go" or nothing at all? 570 // greyed-out "Paste and Go" or nothing at all?
649 AutocompleteTextFieldObserver* observer = [self observer]; 571 AutocompleteTextFieldObserver* observer = [self observer];
650 DCHECK(observer); 572 DCHECK(observer);
651 return observer->CanPasteAndGo(); 573 return observer->CanPasteAndGo();
652 } 574 }
653 return [super validateMenuItem:item]; 575 return [super validateMenuItem:item];
654 } 576 }
655 577
656 - (void)copyToFindPboard:(id)sender { 578 - (void)copyToFindPboard:(id)sender {
657 if (actualSelectedRange_.length == 0) 579 NSRange selectedRange = [self selectedRange];
580 if (selectedRange.length == 0)
658 return; 581 return;
659 NSAttributedString* selection = 582 NSAttributedString* selection =
660 [self attributedSubstringForProposedRange:actualSelectedRange_ 583 [self attributedSubstringForProposedRange:selectedRange
661 actualRange:NULL]; 584 actualRange:NULL];
662 if (!selection) 585 if (!selection)
663 return; 586 return;
664 587
665 [[FindPasteboard sharedInstance] setFindText:[selection string]]; 588 [[FindPasteboard sharedInstance] setFindText:[selection string]];
666 } 589 }
667 590
668 - (void)drawRect:(NSRect)rect { 591 - (void)drawRect:(NSRect)rect {
669 [super drawRect:rect]; 592 [super drawRect:rect];
670 autocomplete_text_field::DrawGrayTextAutocompletion( 593 autocomplete_text_field::DrawGrayTextAutocompletion(
(...skipping 10 matching lines...) Expand all
681 // ThemedWindowDrawing implementation. 604 // ThemedWindowDrawing implementation.
682 605
683 - (void)windowDidChangeTheme { 606 - (void)windowDidChangeTheme {
684 [self updateColorsToMatchTheme]; 607 [self updateColorsToMatchTheme];
685 } 608 }
686 609
687 - (void)windowDidChangeActive { 610 - (void)windowDidChangeActive {
688 } 611 }
689 612
690 @end 613 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698