OLD | NEW |
---|---|
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" | |
7 #include "base/mac/sdk_forward_declarations.h" | 8 #include "base/mac/sdk_forward_declarations.h" |
8 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
9 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
10 #include "chrome/app/chrome_command_ids.h" // IDC_* | 11 #include "chrome/app/chrome_command_ids.h" // IDC_* |
11 #include "chrome/browser/themes/theme_service.h" | 12 #include "chrome/browser/themes/theme_service.h" |
12 #include "chrome/browser/ui/browser_list.h" | 13 #include "chrome/browser/ui/browser_list.h" |
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
14 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" | 15 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" |
15 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" | 16 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" |
16 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" | 17 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" |
(...skipping 25 matching lines...) Expand all Loading... | |
42 BOOL ThePasteboardIsTooDamnBig() { | 43 BOOL ThePasteboardIsTooDamnBig() { |
43 NSPasteboard* pb = [NSPasteboard generalPasteboard]; | 44 NSPasteboard* pb = [NSPasteboard generalPasteboard]; |
44 NSString* type = | 45 NSString* type = |
45 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; | 46 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; |
46 if (!type) | 47 if (!type) |
47 return NO; | 48 return NO; |
48 | 49 |
49 return [[pb stringForType:type] length] > kMaxPasteLength; | 50 return [[pb stringForType:type] length] > kMaxPasteLength; |
50 } | 51 } |
51 | 52 |
53 NSRange VisualSelectionRangeFromRange(NSRange range, NSString* string) { | |
54 if (range.location >= string.length) { | |
55 return range; | |
56 } | |
57 base::string16 text = base::SysNSStringToUTF16(string); | |
58 // TODO(lgrey): Should the break iterator be cached? | |
erikchen
2016/10/12 22:37:48
sounds like premature optimization to me.
lgrey
2016/10/13 15:42:52
Removed comment
| |
59 base::i18n::BreakIterator grapheme_iterator( | |
60 text, base::i18n::BreakIterator::BREAK_CHARACTER); | |
61 if (!grapheme_iterator.Init()) { | |
62 return range; | |
63 } | |
64 while (range.location < text.length() && | |
erikchen
2016/10/12 22:37:48
interesting. This works because NSString's interna
lgrey
2016/10/13 15:42:52
Added a comment. I could also change it to |string
erikchen
2016/10/13 17:08:28
Comments seems good enough.
| |
65 !grapheme_iterator.IsGraphemeBoundary( | |
66 static_cast<size_t>(range.location))) { | |
67 range.location++; | |
68 if (range.length > 0) { | |
69 range.length--; | |
70 } | |
71 } | |
72 return range; | |
73 } | |
74 | |
52 } // namespace | 75 } // namespace |
53 | 76 |
77 // Method exposed for the purpose of overriding. | |
78 // Used to restore model's selection range when the view doesn't | |
79 // match the model due to combining characters. | |
80 // | |
81 // In some cases, (completing 'y' to 'ÿour', for example), the autocomplete | |
82 // system requests a selection range that begins on a combining character. | |
83 // setSelectedRange: and friends document that the range passed to them | |
84 // "must begin and end on glyph boundaries and not split base glyphs and | |
85 // their nonspacing marks". If passed such a range, the selection is | |
86 // expanded to include the original user input, preventing the user | |
87 // from being able to type other words beginning with that letter. | |
88 // | |
89 // To resolve this, the field editor modifies the selection to start | |
90 // on the next glyph boundary, then keeps track of the original and | |
91 // modified selections, substituting the original when the user takes | |
92 // actions that operate on the selection. Since there are many methods | |
93 // in NSResponder (for example deleteToBeginningOfLine:) that operate | |
94 // on the selection, rather than shimming them all, we override this | |
95 // private method that they're implemented in terms of. | |
96 // | |
97 @interface NSTextView (PrivateTextEditing) | |
98 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string; | |
99 @end | |
100 | |
54 @interface AutocompleteTextFieldEditor ()<NSDraggingSource> | 101 @interface AutocompleteTextFieldEditor ()<NSDraggingSource> |
55 @end | 102 @end |
56 | 103 |
57 @implementation AutocompleteTextFieldEditor | 104 @implementation AutocompleteTextFieldEditor |
58 | 105 |
59 - (BOOL)shouldDrawInsertionPoint { | 106 - (BOOL)shouldDrawInsertionPoint { |
60 return [super shouldDrawInsertionPoint] && | 107 return [super shouldDrawInsertionPoint] && |
61 ![[[self delegate] cell] hideFocusState]; | 108 ![[[self delegate] cell] hideFocusState]; |
62 } | 109 } |
63 | 110 |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
393 // (URLDropTarget protocol) | 440 // (URLDropTarget protocol) |
394 - (void)draggingExited:(id<NSDraggingInfo>)sender { | 441 - (void)draggingExited:(id<NSDraggingInfo>)sender { |
395 return [dropHandler_ draggingExited:sender]; | 442 return [dropHandler_ draggingExited:sender]; |
396 } | 443 } |
397 | 444 |
398 // (URLDropTarget protocol) | 445 // (URLDropTarget protocol) |
399 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { | 446 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { |
400 return [dropHandler_ performDragOperation:sender]; | 447 return [dropHandler_ performDragOperation:sender]; |
401 } | 448 } |
402 | 449 |
450 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string { | |
451 if (NSEqualRanges(visualSelectionRange_, range) && | |
452 !NSEqualRanges(visualSelectionRange_, actualSelectionRange_)) { | |
453 range = actualSelectionRange_; | |
454 } | |
455 [super _userReplaceRange:range withString:string]; | |
456 } | |
457 | |
403 // Prevent control characters from being entered into the Omnibox. | 458 // Prevent control characters from being entered into the Omnibox. |
404 // This is invoked for keyboard entry, not for pasting. | 459 // This is invoked for keyboard entry, not for pasting. |
405 - (void)insertText:(id)aString { | 460 - (void)insertText:(id)aString { |
406 AutocompleteTextFieldObserver* observer = [self observer]; | 461 AutocompleteTextFieldObserver* observer = [self observer]; |
407 if (observer) | 462 if (observer) |
408 observer->OnInsertText(); | 463 observer->OnInsertText(); |
409 | 464 |
410 // Repeatedly remove control characters. The loop will only ever | 465 // Repeatedly remove control characters. The loop will only ever |
411 // execute at all when the user enters control characters (using | 466 // execute at all when the user enters control characters (using |
412 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would | 467 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would |
(...skipping 11 matching lines...) Expand all Loading... | |
424 } else { | 479 } else { |
425 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; | 480 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; |
426 while (range.location != NSNotFound) { | 481 while (range.location != NSNotFound) { |
427 aString = | 482 aString = |
428 [aString stringByReplacingCharactersInRange:range withString:@""]; | 483 [aString stringByReplacingCharactersInRange:range withString:@""]; |
429 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; | 484 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; |
430 } | 485 } |
431 DCHECK_EQ(range.length, 0U); | 486 DCHECK_EQ(range.length, 0U); |
432 } | 487 } |
433 | 488 |
434 // NOTE: If |aString| is empty, this intentionally replaces the | 489 if (!NSEqualRanges(visualSelectionRange_, actualSelectionRange_)) { |
435 // selection with empty. This seems consistent with the case where | 490 [super replaceCharactersInRange:actualSelectionRange_ withString:aString]; |
436 // the input contained a mixture of characters and the string ended | 491 } else { |
437 // up not empty. | 492 // NOTE: If |aString| is empty, this intentionally replaces the |
438 [super insertText:aString]; | 493 // selection with empty. This seems consistent with the case where |
494 // the input contained a mixture of characters and the string ended | |
495 // up not empty. | |
496 [super insertText:aString]; | |
497 } | |
439 } | 498 } |
440 | 499 |
441 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange | 500 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange |
442 granularity:(NSSelectionGranularity)granularity { | 501 granularity:(NSSelectionGranularity)granularity { |
443 AutocompleteTextFieldObserver* observer = [self observer]; | 502 AutocompleteTextFieldObserver* observer = [self observer]; |
444 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange | 503 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange |
445 granularity:granularity]; | 504 granularity:granularity]; |
446 if (observer) | 505 if (observer) |
447 return observer->SelectionRangeForProposedRange(modifiedRange); | 506 return observer->SelectionRangeForProposedRange(modifiedRange); |
448 return modifiedRange; | 507 return modifiedRange; |
449 } | 508 } |
450 | 509 |
451 - (void)setSelectedRange:(NSRange)charRange | 510 - (void)setSelectedRange:(NSRange)charRange |
452 affinity:(NSSelectionAffinity)affinity | 511 affinity:(NSSelectionAffinity)affinity |
453 stillSelecting:(BOOL)flag { | 512 stillSelecting:(BOOL)flag { |
454 [super setSelectedRange:charRange affinity:affinity stillSelecting:flag]; | 513 actualSelectionRange_ = charRange; |
514 visualSelectionRange_ = | |
515 VisualSelectionRangeFromRange(charRange, [self string]); | |
516 [super setSelectedRange:visualSelectionRange_ | |
517 affinity:affinity | |
518 stillSelecting:flag]; | |
455 | 519 |
456 // We're only interested in selection changes directly caused by keyboard | 520 // We're only interested in selection changes directly caused by keyboard |
457 // input from the user. | 521 // input from the user. |
458 if (interpretingKeyEvents_) | 522 if (interpretingKeyEvents_) |
459 textChangedByKeyEvents_ = YES; | 523 textChangedByKeyEvents_ = YES; |
460 } | 524 } |
461 | 525 |
526 - (NSRange)selectedRange { | |
527 return actualSelectionRange_; | |
erikchen
2016/10/12 22:37:48
Is there any way we can avoid overriding this meth
lgrey
2016/10/13 15:42:51
We could expose a new method like |actualRange| or
erikchen
2016/10/13 17:08:28
If the only reason this code is necessary is to pr
lgrey
2016/10/13 21:03:56
Exposed actualSelectedRange_ as a property and use
| |
528 } | |
529 | |
462 - (void)interpretKeyEvents:(NSArray *)eventArray { | 530 - (void)interpretKeyEvents:(NSArray *)eventArray { |
463 DCHECK(!interpretingKeyEvents_); | 531 DCHECK(!interpretingKeyEvents_); |
464 interpretingKeyEvents_ = YES; | 532 interpretingKeyEvents_ = YES; |
465 textChangedByKeyEvents_ = NO; | 533 textChangedByKeyEvents_ = NO; |
466 AutocompleteTextFieldObserver* observer = [self observer]; | 534 AutocompleteTextFieldObserver* observer = [self observer]; |
467 | 535 |
468 if (observer) | 536 if (observer) |
469 observer->OnBeforeChange(); | 537 observer->OnBeforeChange(); |
470 | 538 |
471 [super interpretKeyEvents:eventArray]; | 539 [super interpretKeyEvents:eventArray]; |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
604 // ThemedWindowDrawing implementation. | 672 // ThemedWindowDrawing implementation. |
605 | 673 |
606 - (void)windowDidChangeTheme { | 674 - (void)windowDidChangeTheme { |
607 [self updateColorsToMatchTheme]; | 675 [self updateColorsToMatchTheme]; |
608 } | 676 } |
609 | 677 |
610 - (void)windowDidChangeActive { | 678 - (void)windowDidChangeActive { |
611 } | 679 } |
612 | 680 |
613 @end | 681 @end |
OLD | NEW |