| 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 "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" | 5 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" |
| 6 | 6 |
| 7 #include <Carbon/Carbon.h> // kVK_Return | 7 #include <Carbon/Carbon.h> // kVK_Return |
| 8 | 8 |
| 9 #include "base/mac/foundation_util.h" | 9 #include "base/mac/foundation_util.h" |
| 10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 Profile* profile, | 200 Profile* profile, |
| 201 CommandUpdater* command_updater, | 201 CommandUpdater* command_updater, |
| 202 AutocompleteTextField* field) | 202 AutocompleteTextField* field) |
| 203 : OmniboxView( | 203 : OmniboxView( |
| 204 controller, | 204 controller, |
| 205 base::WrapUnique(new ChromeOmniboxClient(controller, profile))), | 205 base::WrapUnique(new ChromeOmniboxClient(controller, profile))), |
| 206 profile_(profile), | 206 profile_(profile), |
| 207 popup_view_(new OmniboxPopupViewMac(this, model(), field)), | 207 popup_view_(new OmniboxPopupViewMac(this, model(), field)), |
| 208 field_(field), | 208 field_(field), |
| 209 saved_temporary_selection_(NSMakeRange(0, 0)), | 209 saved_temporary_selection_(NSMakeRange(0, 0)), |
| 210 selection_before_change_(NSMakeRange(0, 0)), | |
| 211 marked_range_before_change_(NSMakeRange(0, 0)), | 210 marked_range_before_change_(NSMakeRange(0, 0)), |
| 212 delete_was_pressed_(false), | 211 delete_was_pressed_(false), |
| 213 delete_at_end_pressed_(false), | 212 delete_at_end_pressed_(false), |
| 214 in_coalesced_update_block_(false), | 213 in_coalesced_update_block_(false), |
| 215 do_coalesced_text_update_(false), | 214 do_coalesced_text_update_(false), |
| 216 do_coalesced_range_update_(false) { | 215 do_coalesced_range_update_(false) { |
| 217 [field_ setObserver:this]; | 216 [field_ setObserver:this]; |
| 218 | 217 |
| 219 // Needed so that editing doesn't lose the styling. | 218 // Needed so that editing doesn't lose the styling. |
| 220 [field_ setAllowsEditingTextAttributes:YES]; | 219 [field_ setAllowsEditingTextAttributes:YES]; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 DCHECK_LE(caret_pos, text.size()); | 373 DCHECK_LE(caret_pos, text.size()); |
| 375 SetTextAndSelectedRange(text, NSMakeRange(caret_pos, 0)); | 374 SetTextAndSelectedRange(text, NSMakeRange(caret_pos, 0)); |
| 376 | 375 |
| 377 if (update_popup) | 376 if (update_popup) |
| 378 UpdatePopup(); | 377 UpdatePopup(); |
| 379 | 378 |
| 380 if (notify_text_changed) | 379 if (notify_text_changed) |
| 381 TextChanged(); | 380 TextChanged(); |
| 382 } | 381 } |
| 383 | 382 |
| 384 void OmniboxViewMac::SetForcedQuery() { | 383 void OmniboxViewMac::EnterKeywordModeForDefaultSearchProvider() { |
| 385 // We need to do this first, else |SetSelectedRange()| won't work. | 384 // We need to do this first, else |SetSelectedRange()| won't work. |
| 386 FocusLocation(true); | 385 FocusLocation(true); |
| 387 | 386 |
| 388 const base::string16 current_text(GetText()); | 387 // Transition the user into keyword mode using their default search provider. |
| 389 const size_t start = current_text.find_first_not_of(base::kWhitespaceUTF16); | 388 // Select their query if they typed one. |
| 390 if (start == base::string16::npos || (current_text[start] != '?')) { | 389 model()->EnterKeywordModeForDefaultSearchProvider( |
| 391 SetUserText(base::ASCIIToUTF16("?")); | 390 KeywordModeEntryMethod::KEYBOARD_SHORTCUT); |
| 392 } else { | 391 SelectAll(false); |
| 393 NSRange range = NSMakeRange(start + 1, current_text.size() - start - 1); | |
| 394 [[field_ currentEditor] setSelectedRange:range]; | |
| 395 } | |
| 396 } | 392 } |
| 397 | 393 |
| 398 bool OmniboxViewMac::IsSelectAll() const { | 394 bool OmniboxViewMac::IsSelectAll() const { |
| 399 if (![field_ currentEditor]) | 395 if (![field_ currentEditor]) |
| 400 return true; | 396 return true; |
| 401 const NSRange all_range = NSMakeRange(0, GetTextLength()); | 397 const NSRange all_range = NSMakeRange(0, GetTextLength()); |
| 402 return NSEqualRanges(all_range, GetSelectedRange()); | 398 return NSEqualRanges(all_range, GetSelectedRange()); |
| 403 } | 399 } |
| 404 | 400 |
| 405 bool OmniboxViewMac::DeleteAtEndPressed() { | 401 bool OmniboxViewMac::DeleteAtEndPressed() { |
| (...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 678 } | 674 } |
| 679 | 675 |
| 680 bool OmniboxViewMac::IsFirstResponder() const { | 676 bool OmniboxViewMac::IsFirstResponder() const { |
| 681 return [field_ currentEditor] != nil ? true : false; | 677 return [field_ currentEditor] != nil ? true : false; |
| 682 } | 678 } |
| 683 | 679 |
| 684 void OmniboxViewMac::OnBeforePossibleChange() { | 680 void OmniboxViewMac::OnBeforePossibleChange() { |
| 685 // We should only arrive here when the field is focused. | 681 // We should only arrive here when the field is focused. |
| 686 DCHECK(IsFirstResponder()); | 682 DCHECK(IsFirstResponder()); |
| 687 | 683 |
| 688 selection_before_change_ = GetSelectedRange(); | 684 GetState(&state_before_change_); |
| 689 text_before_change_ = GetText(); | |
| 690 marked_range_before_change_ = GetMarkedRange(); | 685 marked_range_before_change_ = GetMarkedRange(); |
| 691 } | 686 } |
| 692 | 687 |
| 693 bool OmniboxViewMac::OnAfterPossibleChange(bool allow_keyword_ui_change) { | 688 bool OmniboxViewMac::OnAfterPossibleChange(bool allow_keyword_ui_change) { |
| 694 // We should only arrive here when the field is focused. | 689 // We should only arrive here when the field is focused. |
| 695 DCHECK(IsFirstResponder()); | 690 DCHECK(IsFirstResponder()); |
| 696 | 691 |
| 697 const NSRange new_selection(GetSelectedRange()); | 692 State new_state; |
| 698 const base::string16 new_text(GetText()); | 693 GetState(&new_state); |
| 699 const size_t length = new_text.length(); | 694 OmniboxViewStateChanges state_changes = |
| 695 GetStateChanges(state_before_change_, new_state); |
| 700 | 696 |
| 701 const bool selection_differs = | 697 const bool at_end_of_edit = (new_state.text.length() == new_state.sel_end); |
| 702 (new_selection.length || selection_before_change_.length) && | |
| 703 !NSEqualRanges(new_selection, selection_before_change_); | |
| 704 const bool at_end_of_edit = (length == new_selection.location); | |
| 705 const bool text_differs = (new_text != text_before_change_) || | |
| 706 !NSEqualRanges(marked_range_before_change_, GetMarkedRange()); | |
| 707 | |
| 708 // When the user has deleted text, we don't allow inline | |
| 709 // autocomplete. This is assumed if the text has gotten shorter AND | |
| 710 // the selection has shifted towards the front of the text. During | |
| 711 // normal typing the text will almost always be shorter (as the new | |
| 712 // input replaces the autocomplete suggestion), but in that case the | |
| 713 // selection point will have moved towards the end of the text. | |
| 714 // TODO(shess): In our implementation, we can catch -deleteBackward: | |
| 715 // and other methods to provide positive knowledge that a delete | |
| 716 // occurred, rather than intuiting it from context. Consider whether | |
| 717 // that would be a stronger approach. | |
| 718 const bool just_deleted_text = | |
| 719 (length < text_before_change_.length() && | |
| 720 new_selection.location <= selection_before_change_.location); | |
| 721 | 698 |
| 722 delete_at_end_pressed_ = false; | 699 delete_at_end_pressed_ = false; |
| 723 | 700 |
| 724 const bool something_changed = model()->OnAfterPossibleChange( | 701 const bool something_changed = model()->OnAfterPossibleChange( |
| 725 text_before_change_, new_text, new_selection.location, | 702 state_changes, allow_keyword_ui_change && !IsImeComposing()); |
| 726 NSMaxRange(new_selection), selection_differs, text_differs, | |
| 727 just_deleted_text, allow_keyword_ui_change && !IsImeComposing()); | |
| 728 | 703 |
| 729 if (delete_was_pressed_ && at_end_of_edit) | 704 if (delete_was_pressed_ && at_end_of_edit) |
| 730 delete_at_end_pressed_ = true; | 705 delete_at_end_pressed_ = true; |
| 731 | 706 |
| 732 // Restyle in case the user changed something. | 707 // Restyle in case the user changed something. |
| 733 // TODO(shess): I believe there are multiple-redraw cases, here. | 708 // TODO(shess): I believe there are multiple-redraw cases, here. |
| 734 // Linux watches for something_changed && text_differs, but that | 709 // Linux watches for something_changed && text_differs, but that |
| 735 // fails for us in case you copy the URL and paste the identical URL | 710 // fails for us in case you copy the URL and paste the identical URL |
| 736 // back (we'll lose the styling). | 711 // back (we'll lose the styling). |
| 737 TextChanged(); | 712 TextChanged(); |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 865 return true; | 840 return true; |
| 866 } | 841 } |
| 867 | 842 |
| 868 if (cmd == @selector(cancelOperation:)) { | 843 if (cmd == @selector(cancelOperation:)) { |
| 869 return model()->OnEscapeKeyPressed(); | 844 return model()->OnEscapeKeyPressed(); |
| 870 } | 845 } |
| 871 | 846 |
| 872 if ((cmd == @selector(insertTab:) || | 847 if ((cmd == @selector(insertTab:) || |
| 873 cmd == @selector(insertTabIgnoringFieldEditor:)) && | 848 cmd == @selector(insertTabIgnoringFieldEditor:)) && |
| 874 model()->is_keyword_hint()) { | 849 model()->is_keyword_hint()) { |
| 875 return model()->AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_TAB); | 850 return model()->AcceptKeyword(KeywordModeEntryMethod::TAB); |
| 876 } | 851 } |
| 877 | 852 |
| 878 // |-noop:| is sent when the user presses Cmd+Return. Override the no-op | 853 // |-noop:| is sent when the user presses Cmd+Return. Override the no-op |
| 879 // behavior with the proper WindowOpenDisposition. | 854 // behavior with the proper WindowOpenDisposition. |
| 880 NSEvent* event = [NSApp currentEvent]; | 855 NSEvent* event = [NSApp currentEvent]; |
| 881 if (cmd == @selector(insertNewline:) || | 856 if (cmd == @selector(insertNewline:) || |
| 882 (cmd == @selector(noop:) && | 857 (cmd == @selector(noop:) && |
| 883 ([event type] == NSKeyDown || [event type] == NSKeyUp) && | 858 ([event type] == NSKeyDown || [event type] == NSKeyUp) && |
| 884 [event keyCode] == kVK_Return)) { | 859 [event keyCode] == kVK_Return)) { |
| 885 WindowOpenDisposition disposition = | 860 WindowOpenDisposition disposition = |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1018 // from the Chrome-specific code. | 993 // from the Chrome-specific code. |
| 1019 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]); | 994 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]); |
| 1020 const NSRange selectedRange = GetSelectedRange(); | 995 const NSRange selectedRange = GetSelectedRange(); |
| 1021 if ([editor shouldChangeTextInRange:selectedRange replacementString:s]) { | 996 if ([editor shouldChangeTextInRange:selectedRange replacementString:s]) { |
| 1022 // Record this paste, so we can do different behavior. | 997 // Record this paste, so we can do different behavior. |
| 1023 model()->OnPaste(); | 998 model()->OnPaste(); |
| 1024 | 999 |
| 1025 // Force a Paste operation to trigger the text_changed code in | 1000 // Force a Paste operation to trigger the text_changed code in |
| 1026 // OnAfterPossibleChange(), even if identical contents are pasted | 1001 // OnAfterPossibleChange(), even if identical contents are pasted |
| 1027 // into the text box. | 1002 // into the text box. |
| 1028 text_before_change_.clear(); | 1003 state_before_change_.text.clear(); |
| 1029 | 1004 |
| 1030 [editor replaceCharactersInRange:selectedRange withString:s]; | 1005 [editor replaceCharactersInRange:selectedRange withString:s]; |
| 1031 [editor didChangeText]; | 1006 [editor didChangeText]; |
| 1032 } | 1007 } |
| 1033 } | 1008 } |
| 1034 | 1009 |
| 1035 // TODO(dominich): Move to OmniboxView base class? Currently this is defined on | 1010 // TODO(dominich): Move to OmniboxView base class? Currently this is defined on |
| 1036 // the AutocompleteTextFieldObserver but the logic is shared between all | 1011 // the AutocompleteTextFieldObserver but the logic is shared between all |
| 1037 // platforms. Some refactor might be necessary to simplify this. Or at least | 1012 // platforms. Some refactor might be necessary to simplify this. Or at least |
| 1038 // this method could call the OmniboxView version. | 1013 // this method could call the OmniboxView version. |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1161 display_text); | 1136 display_text); |
| 1162 NSDictionary* notification_info = @{ | 1137 NSDictionary* notification_info = @{ |
| 1163 NSAccessibilityAnnouncementKey : announcement, | 1138 NSAccessibilityAnnouncementKey : announcement, |
| 1164 NSAccessibilityPriorityKey : @(NSAccessibilityPriorityHigh) | 1139 NSAccessibilityPriorityKey : @(NSAccessibilityPriorityHigh) |
| 1165 }; | 1140 }; |
| 1166 NSAccessibilityPostNotificationWithUserInfo( | 1141 NSAccessibilityPostNotificationWithUserInfo( |
| 1167 [field_ window], | 1142 [field_ window], |
| 1168 NSAccessibilityAnnouncementRequestedNotification, | 1143 NSAccessibilityAnnouncementRequestedNotification, |
| 1169 notification_info); | 1144 notification_info); |
| 1170 } | 1145 } |
| OLD | NEW |