diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-28 20:24:00 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-28 20:24:00 +0000 |
commit | 939404935277cd0b4ecbe606781297361383bff0 (patch) | |
tree | e0230f309d2a90a4ee24ff22961d5aa4c4cd8982 /chrome/browser/autocomplete/autocomplete_edit_view_mac.mm | |
parent | 7e1bcdd086adb95c12c6abb0bb8f70a7e702e797 (diff) | |
download | chromium_src-939404935277cd0b4ecbe606781297361383bff0.zip chromium_src-939404935277cd0b4ecbe606781297361383bff0.tar.gz chromium_src-939404935277cd0b4ecbe606781297361383bff0.tar.bz2 |
cocoa: Move AutocompleteEditViewMac/autocomplete_edit_view_mac.* to ui/cocoa/omnibox directory.
- Rename AutocompleteEditViewMac to OmniboxViewMac.
- Move autocomplete_edit_view_mac.* to omnibox_view_mac.*
BUG=80186
TEST=None
R=pkasting@chromium.org
Review URL: http://codereview.chromium.org/6904049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83387 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autocomplete/autocomplete_edit_view_mac.mm')
-rw-r--r-- | chrome/browser/autocomplete/autocomplete_edit_view_mac.mm | 1125 |
1 files changed, 0 insertions, 1125 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm deleted file mode 100644 index 69d5afe6..0000000 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm +++ /dev/null @@ -1,1125 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" - -#include <Carbon/Carbon.h> // kVK_Return - -#include "app/mac/nsimage_cache.h" -#include "base/string_util.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/autocomplete/autocomplete_edit.h" -#include "chrome/browser/autocomplete/autocomplete_match.h" -#include "chrome/browser/autocomplete/autocomplete_popup_model.h" -#include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/ui/cocoa/event_utils.h" -#include "chrome/browser/ui/toolbar/toolbar_model.h" -#include "content/browser/tab_contents/tab_contents.h" -#include "grit/generated_resources.h" -#include "grit/theme_resources.h" -#include "grit/theme_resources_standard.h" -#include "net/base/escape.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" -#include "ui/base/clipboard/clipboard.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/image.h" -#include "ui/gfx/rect.h" - -// Focus-handling between |field_| and |model_| is a bit subtle. -// Other platforms detect change of focus, which is inconvenient -// without subclassing NSTextField (even with a subclass, the use of a -// field editor may complicate things). -// -// |model_| doesn't actually do anything when it gains focus, it just -// initializes. Visible activity happens only after the user edits. -// NSTextField delegate receives messages around starting and ending -// edits, so that suffices to catch focus changes. Since all calls -// into |model_| start from AutocompleteEditViewMac, in the worst case -// we can add code to sync up the sense of focus as needed. -// -// I've added DCHECK(IsFirstResponder()) in the places which I believe -// should only be reachable when |field_| is being edited. If these -// fire, it probably means someone unexpected is calling into -// |model_|. -// -// Other platforms don't appear to have the sense of "key window" that -// Mac does (I believe their fields lose focus when the window loses -// focus). Rather than modifying focus outside the control's edit -// scope, when the window resigns key the autocomplete popup is -// closed. |model_| still believes it has focus, and the popup will -// be regenerated on the user's next edit. That seems to match how -// things work on other platforms. - -namespace { - -// TODO(shess): This is ugly, find a better way. Using it right now -// so that I can crib from gtk and still be able to see that I'm using -// the same values easily. -NSColor* ColorWithRGBBytes(int rr, int gg, int bb) { - DCHECK_LE(rr, 255); - DCHECK_LE(bb, 255); - DCHECK_LE(gg, 255); - return [NSColor colorWithCalibratedRed:static_cast<float>(rr)/255.0 - green:static_cast<float>(gg)/255.0 - blue:static_cast<float>(bb)/255.0 - alpha:1.0]; -} - -NSColor* HostTextColor() { - return [NSColor blackColor]; -} -NSColor* BaseTextColor() { - return [NSColor darkGrayColor]; -} -NSColor* SuggestTextColor() { - return [NSColor grayColor]; -} -NSColor* SecureSchemeColor() { - return ColorWithRGBBytes(0x07, 0x95, 0x00); -} -NSColor* SecurityErrorSchemeColor() { - return ColorWithRGBBytes(0xa2, 0x00, 0x00); -} - -// Store's the model and view state across tab switches. -struct AutocompleteEditViewMacState { - AutocompleteEditViewMacState(const AutocompleteEditModel::State model_state, - const bool has_focus, const NSRange& selection) - : model_state(model_state), - has_focus(has_focus), - selection(selection) { - } - - const AutocompleteEditModel::State model_state; - const bool has_focus; - const NSRange selection; -}; - -// Returns a lazily initialized property bag accessor for saving our -// state in a TabContents. When constructed |accessor| generates a -// globally-unique id used to index into the per-tab PropertyBag used -// to store the state data. -PropertyAccessor<AutocompleteEditViewMacState>* GetStateAccessor() { - static PropertyAccessor<AutocompleteEditViewMacState> accessor; - return &accessor; -} - -// Accessors for storing and getting the state from the tab. -void StoreStateToTab(TabContents* tab, - const AutocompleteEditViewMacState& state) { - GetStateAccessor()->SetProperty(tab->property_bag(), state); -} -const AutocompleteEditViewMacState* GetStateFromTab(const TabContents* tab) { - return GetStateAccessor()->GetProperty(tab->property_bag()); -} - -// Helper to make converting url_parse ranges to NSRange easier to -// read. -NSRange ComponentToNSRange(const url_parse::Component& component) { - return NSMakeRange(static_cast<NSInteger>(component.begin), - static_cast<NSInteger>(component.len)); -} - -} // namespace - -// static -NSImage* AutocompleteEditViewMac::ImageForResource(int resource_id) { - NSString* image_name = nil; - - switch(resource_id) { - // From the autocomplete popup, or the star icon at the RHS of the - // text field. - case IDR_STAR: image_name = @"star.pdf"; break; - case IDR_STAR_LIT: image_name = @"star_lit.pdf"; break; - - // Values from |AutocompleteMatch::TypeToIcon()|. - case IDR_OMNIBOX_SEARCH: - image_name = @"omnibox_search.pdf"; break; - case IDR_OMNIBOX_HTTP: - image_name = @"omnibox_http.pdf"; break; - case IDR_OMNIBOX_HISTORY: - image_name = @"omnibox_history.pdf"; break; - case IDR_OMNIBOX_EXTENSION_APP: - image_name = @"omnibox_extension_app.pdf"; break; - - // Values from |ToolbarModel::GetIcon()|. - case IDR_OMNIBOX_HTTPS_VALID: - image_name = @"omnibox_https_valid.pdf"; break; - case IDR_OMNIBOX_HTTPS_WARNING: - image_name = @"omnibox_https_warning.pdf"; break; - case IDR_OMNIBOX_HTTPS_INVALID: - image_name = @"omnibox_https_invalid.pdf"; break; - } - - if (image_name) { - if (NSImage* image = app::mac::GetCachedImageWithName(image_name)) { - return image; - } else { - NOTREACHED() - << "Missing image for " << base::SysNSStringToUTF8(image_name); - } - } - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - return rb.GetNativeImageNamed(resource_id); -} - -AutocompleteEditViewMac::AutocompleteEditViewMac( - AutocompleteEditController* controller, - ToolbarModel* toolbar_model, - Profile* profile, - CommandUpdater* command_updater, - AutocompleteTextField* field) - : model_(new AutocompleteEditModel(this, controller, profile)), - popup_view_(new AutocompletePopupViewMac(this, model_.get(), profile, - field)), - controller_(controller), - toolbar_model_(toolbar_model), - command_updater_(command_updater), - field_(field), - suggest_text_length_(0), - delete_was_pressed_(false), - delete_at_end_pressed_(false), - line_height_(0) { - DCHECK(controller); - DCHECK(toolbar_model); - DCHECK(profile); - DCHECK(command_updater); - DCHECK(field); - [field_ setObserver:this]; - - // Needed so that editing doesn't lose the styling. - [field_ setAllowsEditingTextAttributes:YES]; - - // Get the appropriate line height for the font that we use. - scoped_nsobject<NSLayoutManager> - layoutManager([[NSLayoutManager alloc] init]); - [layoutManager setUsesScreenFonts:YES]; - line_height_ = [layoutManager defaultLineHeightForFont:GetFieldFont()]; - DCHECK_GT(line_height_, 0); -} - -AutocompleteEditViewMac::~AutocompleteEditViewMac() { - // Destroy popup view before this object in case it tries to call us - // back in the destructor. Likewise for destroying the model before - // this object. - popup_view_.reset(); - model_.reset(); - - // Disconnect from |field_|, it outlives this object. - [field_ setObserver:NULL]; -} - -AutocompleteEditModel* AutocompleteEditViewMac::model() { - return model_.get(); -} - -const AutocompleteEditModel* AutocompleteEditViewMac::model() const { - return model_.get(); -} - -void AutocompleteEditViewMac::SaveStateToTab(TabContents* tab) { - DCHECK(tab); - - const bool hasFocus = [field_ currentEditor] ? true : false; - - NSRange range; - if (hasFocus) { - range = GetSelectedRange(); - } else { - // If we are not focussed, there is no selection. Manufacture - // something reasonable in case it starts to matter in the future. - range = NSMakeRange(0, GetTextLength()); - } - - AutocompleteEditViewMacState state(model_->GetStateForTabSwitch(), - hasFocus, range); - StoreStateToTab(tab, state); -} - -void AutocompleteEditViewMac::Update( - const TabContents* tab_for_state_restoring) { - // TODO(shess): It seems like if the tab is non-NULL, then this code - // shouldn't need to be called at all. When coded that way, I find - // that the field isn't always updated correctly. Figure out why - // this is. Maybe this method should be refactored into more - // specific cases. - const bool user_visible = - model_->UpdatePermanentText(WideToUTF16Hack(toolbar_model_->GetText())); - - if (tab_for_state_restoring) { - RevertAll(); - - const AutocompleteEditViewMacState* state = - GetStateFromTab(tab_for_state_restoring); - if (state) { - // Should restore the user's text via SetUserText(). - model_->RestoreState(state->model_state); - - // Restore focus and selection if they were present when the tab - // was switched away. - if (state->has_focus) { - // TODO(shess): Unfortunately, there is no safe way to update - // this because TabStripController -selectTabWithContents:* is - // also messing with focus. Both parties need to agree to - // store existing state before anyone tries to setup the new - // state. Anyhow, it would look something like this. -#if 0 - [[field_ window] makeFirstResponder:field_]; - [[field_ currentEditor] setSelectedRange:state->selection]; -#endif - } - } - } else if (user_visible) { - // Restore everything to the baseline look. - RevertAll(); - // TODO(shess): Figure out how this case is used, to make sure - // we're getting the selection and popup right. - - } else { - // TODO(shess): This corresponds to _win and _gtk, except those - // guard it with a test for whether the security level changed. - // But AFAICT, that can only change if the text changed, and that - // code compares the toolbar_model_ security level with the local - // security level. Dig in and figure out why this isn't a no-op - // that should go away. - EmphasizeURLComponents(); - } -} - -void AutocompleteEditViewMac::OpenURL(const GURL& url, - WindowOpenDisposition disposition, - PageTransition::Type transition, - const GURL& alternate_nav_url, - size_t selected_line, - const string16& keyword) { - // TODO(shess): Why is the caller passing an invalid url in the - // first place? Make sure that case isn't being dropped on the - // floor. - if (!url.is_valid()) { - return; - } - - model_->OpenURL(url, disposition, transition, alternate_nav_url, - selected_line, keyword); -} - -string16 AutocompleteEditViewMac::GetText() const { - return base::SysNSStringToUTF16(GetNonSuggestTextSubstring()); -} - -bool AutocompleteEditViewMac::IsEditingOrEmpty() const { - return model_->user_input_in_progress() || !GetTextLength(); -} - -int AutocompleteEditViewMac::GetIcon() const { - return IsEditingOrEmpty() ? - AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) : - toolbar_model_->GetIcon(); -} - -void AutocompleteEditViewMac::SetUserText(const string16& text) { - SetUserText(text, text, true); -} - -void AutocompleteEditViewMac::SetUserText(const string16& text, - const string16& display_text, - bool update_popup) { - model_->SetUserText(text); - // TODO(shess): TODO below from gtk. - // TODO(deanm): something about selection / focus change here. - SetText(display_text); - if (update_popup) { - UpdatePopup(); - } - model_->OnChanged(); -} - -NSRange AutocompleteEditViewMac::GetSelectedRange() const { - return [[field_ currentEditor] selectedRange]; -} - -NSRange AutocompleteEditViewMac::GetMarkedRange() const { - DCHECK([field_ currentEditor]); - return [(NSTextView*)[field_ currentEditor] markedRange]; -} - -void AutocompleteEditViewMac::SetSelectedRange(const NSRange range) { - // This can be called when we don't have focus. For instance, when - // the user clicks the "Go" button. - if (model_->has_focus()) { - // TODO(shess): If |model_| thinks we have focus, this should not - // be necessary. Try to convert to DCHECK(IsFirstResponder()). - if (![field_ currentEditor]) { - [[field_ window] makeFirstResponder:field_]; - } - - // TODO(shess): What if it didn't get first responder, and there is - // no field editor? This will do nothing. Well, at least it won't - // crash. Think of something more productive to do, or prove that - // it cannot occur and DCHECK appropriately. - [[field_ currentEditor] setSelectedRange:range]; - } -} - -void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const string16& text, - size_t caret_pos) { - DCHECK_LE(caret_pos, text.size()); - SetTextAndSelectedRange(text, NSMakeRange(caret_pos, caret_pos)); -} - -void AutocompleteEditViewMac::SetForcedQuery() { - // We need to do this first, else |SetSelectedRange()| won't work. - FocusLocation(true); - - const string16 current_text(GetText()); - const size_t start = current_text.find_first_not_of(kWhitespaceUTF16); - if (start == string16::npos || (current_text[start] != '?')) { - SetUserText(ASCIIToUTF16("?")); - } else { - NSRange range = NSMakeRange(start + 1, current_text.size() - start - 1); - [[field_ currentEditor] setSelectedRange:range]; - } -} - -bool AutocompleteEditViewMac::IsSelectAll() { - if (![field_ currentEditor]) - return true; - const NSRange all_range = NSMakeRange(0, GetTextLength()); - return NSEqualRanges(all_range, GetSelectedRange()); -} - -bool AutocompleteEditViewMac::DeleteAtEndPressed() { - return delete_at_end_pressed_; -} - -void AutocompleteEditViewMac::GetSelectionBounds(string16::size_type* start, - string16::size_type* end) { - if (![field_ currentEditor]) { - *start = *end = 0; - return; - } - - const NSRange selected_range = GetSelectedRange(); - *start = static_cast<size_t>(selected_range.location); - *end = static_cast<size_t>(NSMaxRange(selected_range)); -} - -void AutocompleteEditViewMac::SelectAll(bool reversed) { - // TODO(shess): Figure out what |reversed| implies. The gtk version - // has it imply inverting the selection front to back, but I don't - // even know if that makes sense for Mac. - - // TODO(shess): Verify that we should be stealing focus at this - // point. - SetSelectedRange(NSMakeRange(0, GetTextLength())); -} - -void AutocompleteEditViewMac::RevertAll() { - ClosePopup(); - model_->Revert(); - model_->OnChanged(); - [field_ clearUndoChain]; -} - -void AutocompleteEditViewMac::UpdatePopup() { - model_->SetInputInProgress(true); - if (!model_->has_focus()) - return; - - // Comment copied from OmniboxViewWin::UpdatePopup(): - // Don't inline autocomplete when: - // * The user is deleting text - // * The caret/selection isn't at the end of the text - // * The user has just pasted in something that replaced all the text - // * The user is trying to compose something in an IME - bool prevent_inline_autocomplete = IsImeComposing(); - NSTextView* editor = (NSTextView*)[field_ currentEditor]; - if (editor) { - if (NSMaxRange([editor selectedRange]) < - [[editor textStorage] length] - suggest_text_length_) - prevent_inline_autocomplete = true; - } - - model_->StartAutocomplete([editor selectedRange].length != 0, - prevent_inline_autocomplete); -} - -void AutocompleteEditViewMac::ClosePopup() { - model_->StopAutocomplete(); -} - -void AutocompleteEditViewMac::SetFocus() { -} - -void AutocompleteEditViewMac::SetText(const string16& display_text) { - // If we are setting the text directly, there cannot be any suggest text. - suggest_text_length_ = 0; - SetTextInternal(display_text); -} - -void AutocompleteEditViewMac::SetTextInternal( - const string16& display_text) { - NSString* ss = base::SysUTF16ToNSString(display_text); - NSMutableAttributedString* as = - [[[NSMutableAttributedString alloc] initWithString:ss] autorelease]; - - ApplyTextAttributes(display_text, as); - - [field_ setAttributedStringValue:as]; - - // TODO(shess): This may be an appropriate place to call: - // model_->OnChanged(); - // In the current implementation, this tells LocationBarViewMac to - // mess around with |model_| and update |field_|. Unfortunately, - // when I look at our peer implementations, it's not entirely clear - // to me if this is safe. SetTextInternal() is sort of an utility method, - // and different callers sometimes have different needs. Research - // this issue so that it can be added safely. - - // TODO(shess): Also, consider whether this code couldn't just - // manage things directly. Windows uses a series of overlaid view - // objects to accomplish the hinting stuff that OnChanged() does, so - // it makes sense to have it in the controller that lays those - // things out. Mac instead pushes the support into a custom - // text-field implementation. -} - -void AutocompleteEditViewMac::SetTextAndSelectedRange( - const string16& display_text, const NSRange range) { - SetText(display_text); - SetSelectedRange(range); -} - -NSString* AutocompleteEditViewMac::GetNonSuggestTextSubstring() const { - NSString* text = [field_ stringValue]; - if (suggest_text_length_ > 0) { - NSUInteger length = [text length]; - - DCHECK_LE(suggest_text_length_, length); - text = [text substringToIndex:(length - suggest_text_length_)]; - } - return text; -} - -NSString* AutocompleteEditViewMac::GetSuggestTextSubstring() const { - if (suggest_text_length_ == 0) - return nil; - - NSString* text = [field_ stringValue]; - NSUInteger length = [text length]; - DCHECK_LE(suggest_text_length_, length); - return [text substringFromIndex:(length - suggest_text_length_)]; -} - -void AutocompleteEditViewMac::EmphasizeURLComponents() { - NSTextView* editor = (NSTextView*)[field_ currentEditor]; - // If the autocomplete text field is in editing mode, then we can just change - // its attributes through its editor. Otherwise, we simply reset its content. - if (editor) { - NSTextStorage* storage = [editor textStorage]; - [storage beginEditing]; - - // Clear the existing attributes from the text storage, then - // overlay the appropriate Omnibox attributes. - [storage setAttributes:[NSDictionary dictionary] - range:NSMakeRange(0, [storage length])]; - ApplyTextAttributes(GetText(), storage); - - [storage endEditing]; - } else { - SetText(GetText()); - } -} - -void AutocompleteEditViewMac::ApplyTextAttributes( - const string16& display_text, NSMutableAttributedString* as) { - [as addAttribute:NSFontAttributeName value:GetFieldFont() - range:NSMakeRange(0, [as length])]; - - // Make a paragraph style locking in the standard line height as the maximum, - // otherwise the baseline may shift "downwards". - scoped_nsobject<NSMutableParagraphStyle> - paragraph_style([[NSMutableParagraphStyle alloc] init]); - [paragraph_style setMaximumLineHeight:line_height_]; - [as addAttribute:NSParagraphStyleAttributeName value:paragraph_style - range:NSMakeRange(0, [as length])]; - - // Grey out the suggest text. - [as addAttribute:NSForegroundColorAttributeName value:SuggestTextColor() - range:NSMakeRange([as length] - suggest_text_length_, - suggest_text_length_)]; - - url_parse::Component scheme, host; - AutocompleteInput::ParseForEmphasizeComponents( - display_text, model_->GetDesiredTLD(), &scheme, &host); - const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0); - if (emphasize) { - [as addAttribute:NSForegroundColorAttributeName value:BaseTextColor() - range:NSMakeRange(0, [as length])]; - - [as addAttribute:NSForegroundColorAttributeName value:HostTextColor() - range:ComponentToNSRange(host)]; - } - - // TODO(shess): GTK has this as a member var, figure out why. - // [Could it be to not change if no change? If so, I'm guessing - // AppKit may already handle that.] - const ToolbarModel::SecurityLevel security_level = - toolbar_model_->GetSecurityLevel(); - - // Emphasize the scheme for security UI display purposes (if necessary). - if (!model_->user_input_in_progress() && scheme.is_nonempty() && - (security_level != ToolbarModel::NONE)) { - NSColor* color; - if (security_level == ToolbarModel::EV_SECURE || - security_level == ToolbarModel::SECURE) { - color = SecureSchemeColor(); - } else if (security_level == ToolbarModel::SECURITY_ERROR) { - color = SecurityErrorSchemeColor(); - // Add a strikethrough through the scheme. - [as addAttribute:NSStrikethroughStyleAttributeName - value:[NSNumber numberWithInt:NSUnderlineStyleSingle] - range:ComponentToNSRange(scheme)]; - } else if (security_level == ToolbarModel::SECURITY_WARNING) { - color = BaseTextColor(); - } else { - NOTREACHED(); - color = BaseTextColor(); - } - [as addAttribute:NSForegroundColorAttributeName value:color - range:ComponentToNSRange(scheme)]; - } -} - -void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( - const string16& display_text, bool save_original_selection) { - if (save_original_selection) - saved_temporary_selection_ = GetSelectedRange(); - - suggest_text_length_ = 0; - SetWindowTextAndCaretPos(display_text, display_text.size()); - model_->OnChanged(); - [field_ clearUndoChain]; -} - -void AutocompleteEditViewMac::OnStartingIME() { - // Reset the suggest text just before starting an IME composition session, - // otherwise the IME composition may be interrupted when the suggest text - // gets reset by the IME composition change. - SetInstantSuggestion(string16(), false); -} - -bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( - const string16& display_text, size_t user_text_length) { - // TODO(shess): Make sure that this actually works. The round trip - // to native form and back may mean that it's the same but not the - // same. - if (display_text == GetText()) - return false; - - DCHECK_LE(user_text_length, display_text.size()); - const NSRange range = - NSMakeRange(user_text_length, display_text.size() - user_text_length); - SetTextAndSelectedRange(display_text, range); - model_->OnChanged(); - [field_ clearUndoChain]; - - return true; -} - -void AutocompleteEditViewMac::OnRevertTemporaryText() { - SetSelectedRange(saved_temporary_selection_); -} - -bool AutocompleteEditViewMac::IsFirstResponder() const { - return [field_ currentEditor] != nil ? true : false; -} - -void AutocompleteEditViewMac::OnBeforePossibleChange() { - // We should only arrive here when the field is focussed. - DCHECK(IsFirstResponder()); - - selection_before_change_ = GetSelectedRange(); - text_before_change_ = GetText(); - marked_range_before_change_ = GetMarkedRange(); -} - -bool AutocompleteEditViewMac::OnAfterPossibleChange() { - // We should only arrive here when the field is focussed. - DCHECK(IsFirstResponder()); - - const NSRange new_selection(GetSelectedRange()); - const string16 new_text(GetText()); - const size_t length = new_text.length(); - - const bool selection_differs = - (new_selection.length || selection_before_change_.length) && - !NSEqualRanges(new_selection, selection_before_change_); - const bool at_end_of_edit = (length == new_selection.location); - const bool text_differs = (new_text != text_before_change_) || - !NSEqualRanges(marked_range_before_change_, GetMarkedRange()); - - // When the user has deleted text, we don't allow inline - // autocomplete. This is assumed if the text has gotten shorter AND - // the selection has shifted towards the front of the text. During - // normal typing the text will almost always be shorter (as the new - // input replaces the autocomplete suggestion), but in that case the - // selection point will have moved towards the end of the text. - // TODO(shess): In our implementation, we can catch -deleteBackward: - // and other methods to provide positive knowledge that a delete - // occured, rather than intuiting it from context. Consider whether - // that would be a stronger approach. - const bool just_deleted_text = - (length < text_before_change_.length() && - new_selection.location <= selection_before_change_.location); - - delete_at_end_pressed_ = false; - - const bool something_changed = model_->OnAfterPossibleChange( - new_text, new_selection.location, NSMaxRange(new_selection), - selection_differs, text_differs, just_deleted_text, - !IsImeComposing()); - - if (delete_was_pressed_ && at_end_of_edit) - delete_at_end_pressed_ = true; - - // Restyle in case the user changed something. - // TODO(shess): I believe there are multiple-redraw cases, here. - // Linux watches for something_changed && text_differs, but that - // fails for us in case you copy the URL and paste the identical URL - // back (we'll lose the styling). - EmphasizeURLComponents(); - model_->OnChanged(); - - delete_was_pressed_ = false; - - return something_changed; -} - -gfx::NativeView AutocompleteEditViewMac::GetNativeView() const { - return field_; -} - -CommandUpdater* AutocompleteEditViewMac::GetCommandUpdater() { - return command_updater_; -} - -void AutocompleteEditViewMac::SetInstantSuggestion( - const string16& suggest_text, - bool animate_to_complete) { - NSString* text = GetNonSuggestTextSubstring(); - bool needs_update = (suggest_text_length_ > 0); - - // Append the new suggest text. - suggest_text_length_ = suggest_text.length(); - if (suggest_text_length_ > 0) { - text = [text stringByAppendingString:base::SysUTF16ToNSString( - suggest_text)]; - needs_update = true; - } - - if (needs_update) { - NSRange current_range = GetSelectedRange(); - SetTextInternal(base::SysNSStringToUTF16(text)); - if (NSMaxRange(current_range) <= [text length] - suggest_text_length_) - SetSelectedRange(current_range); - else - SetSelectedRange(NSMakeRange([text length] - suggest_text_length_, 0)); - } -} - -string16 AutocompleteEditViewMac::GetInstantSuggestion() const { - return suggest_text_length_ ? - base::SysNSStringToUTF16(GetSuggestTextSubstring()) : string16(); -} - -int AutocompleteEditViewMac::TextWidth() const { - // Not used on mac. - NOTREACHED(); - return 0; -} - -bool AutocompleteEditViewMac::IsImeComposing() const { - return [(NSTextView*)[field_ currentEditor] hasMarkedText]; -} - -void AutocompleteEditViewMac::OnDidBeginEditing() { - // We should only arrive here when the field is focussed. - DCHECK([field_ currentEditor]); -} - -void AutocompleteEditViewMac::OnBeforeChange() { - // Capture the current state. - OnBeforePossibleChange(); -} - -void AutocompleteEditViewMac::OnDidChange() { - // Figure out what changed and notify the model_. - OnAfterPossibleChange(); -} - -void AutocompleteEditViewMac::OnDidEndEditing() { - ClosePopup(); -} - -bool AutocompleteEditViewMac::OnDoCommandBySelector(SEL cmd) { - // We should only arrive here when the field is focussed. - DCHECK(IsFirstResponder()); - - if (cmd != @selector(moveRight:) && - cmd != @selector(insertTab:) && - cmd != @selector(insertTabIgnoringFieldEditor:)) { - // Reset the suggest text for any change other than key right or tab. - // TODO(rohitrao): This is here to prevent complications when editing text. - // See if this can be removed. - SetInstantSuggestion(string16(), false); - } - - if (cmd == @selector(deleteForward:)) - delete_was_pressed_ = true; - - // Don't intercept up/down-arrow if the popup isn't open. - if (popup_view_->IsOpen()) { - if (cmd == @selector(moveDown:)) { - model_->OnUpOrDownKeyPressed(1); - return true; - } - - if (cmd == @selector(moveUp:)) { - model_->OnUpOrDownKeyPressed(-1); - return true; - } - } - - if (cmd == @selector(moveRight:)) { - // Only commit suggested text if the cursor is all the way to the right and - // there is no selection. - if (suggest_text_length_ > 0 && IsCaretAtEnd()) { - model_->CommitSuggestedText(true); - return true; - } - } - - if (cmd == @selector(scrollPageDown:)) { - model_->OnUpOrDownKeyPressed(model_->result().size()); - return true; - } - - if (cmd == @selector(scrollPageUp:)) { - model_->OnUpOrDownKeyPressed(-model_->result().size()); - return true; - } - - if (cmd == @selector(cancelOperation:)) { - return model_->OnEscapeKeyPressed(); - } - - if (cmd == @selector(insertTab:) || - cmd == @selector(insertTabIgnoringFieldEditor:)) { - if (model_->is_keyword_hint()) - return model_->AcceptKeyword(); - - if (suggest_text_length_ > 0) { - model_->CommitSuggestedText(true); - return true; - } - - if (!IsCaretAtEnd()) { - PlaceCaretAt(GetTextLength()); - // OnDidChange() will not be triggered when setting selected range in this - // method, so we need to call it explicitly. - OnDidChange(); - return true; - } - - if (model_->AcceptCurrentInstantPreview()) - return true; - } - - // |-noop:| is sent when the user presses Cmd+Return. Override the no-op - // behavior with the proper WindowOpenDisposition. - NSEvent* event = [NSApp currentEvent]; - if (cmd == @selector(insertNewline:) || - (cmd == @selector(noop:) && [event keyCode] == kVK_Return)) { - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent(event); - model_->AcceptInput(disposition, false); - // Opening a URL in a background tab should also revert the omnibox contents - // to their original state. We cannot do a blanket revert in OpenURL() - // because middle-clicks also open in a new background tab, but those should - // not revert the omnibox text. - RevertAll(); - return true; - } - - // Option-Return - if (cmd == @selector(insertNewlineIgnoringFieldEditor:)) { - model_->AcceptInput(NEW_FOREGROUND_TAB, false); - return true; - } - - // When the user does Control-Enter, the existing content has "www." - // prepended and ".com" appended. |model_| should already have - // received notification when the Control key was depressed, but it - // is safe to tell it twice. - if (cmd == @selector(insertLineBreak:)) { - OnControlKeyChanged(true); - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); - model_->AcceptInput(disposition, false); - return true; - } - - if (cmd == @selector(deleteBackward:)) { - if (OnBackspacePressed()) { - return true; - } - } - - if (cmd == @selector(deleteForward:)) { - const NSUInteger modifiers = [[NSApp currentEvent] modifierFlags]; - if ((modifiers & NSShiftKeyMask) != 0) { - if (model_->popup_model()->IsOpen()) { - model_->popup_model()->TryDeletingCurrentItem(); - return true; - } - } - } - - return false; -} - -void AutocompleteEditViewMac::OnSetFocus(bool control_down) { - model_->OnSetFocus(control_down); - controller_->OnSetFocus(); -} - -void AutocompleteEditViewMac::OnKillFocus() { - // Tell the model to reset itself. - model_->OnWillKillFocus(NULL); - model_->OnKillFocus(); - controller_->OnKillFocus(); -} - -bool AutocompleteEditViewMac::CanCopy() { - const NSRange selection = GetSelectedRange(); - return selection.length > 0; -} - -void AutocompleteEditViewMac::CopyToPasteboard(NSPasteboard* pb) { - DCHECK(CanCopy()); - - const NSRange selection = GetSelectedRange(); - string16 text = base::SysNSStringToUTF16( - [[field_ stringValue] substringWithRange:selection]); - - GURL url; - bool write_url = false; - model_->AdjustTextForCopy(selection.location, IsSelectAll(), &text, &url, - &write_url); - - NSString* nstext = base::SysUTF16ToNSString(text); - [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pb setString:nstext forType:NSStringPboardType]; - - if (write_url) { - [pb declareURLPasteboardWithAdditionalTypes:[NSArray array] owner:nil]; - [pb setDataForURL:base::SysUTF8ToNSString(url.spec()) title:nstext]; - } -} - -void AutocompleteEditViewMac::OnPaste() { - // This code currently expects |field_| to be focussed. - DCHECK([field_ currentEditor]); - - string16 text = GetClipboardText(g_browser_process->clipboard()); - if (text.empty()) { - return; - } - NSString* s = base::SysUTF16ToNSString(text); - - // -shouldChangeTextInRange:* and -didChangeText are documented in - // NSTextView as things you need to do if you write additional - // user-initiated editing functions. They cause the appropriate - // delegate methods to be called. - // TODO(shess): It would be nice to separate the Cocoa-specific code - // from the Chrome-specific code. - NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]); - const NSRange selectedRange = GetSelectedRange(); - if ([editor shouldChangeTextInRange:selectedRange replacementString:s]) { - // Record this paste, so we can do different behavior. - model_->on_paste(); - - // Force a Paste operation to trigger the text_changed code in - // OnAfterPossibleChange(), even if identical contents are pasted - // into the text box. - text_before_change_.clear(); - - [editor replaceCharactersInRange:selectedRange withString:s]; - [editor didChangeText]; - } -} - -bool AutocompleteEditViewMac::CanPasteAndGo() { - return - model_->CanPasteAndGo(GetClipboardText(g_browser_process->clipboard())); -} - -int AutocompleteEditViewMac::GetPasteActionStringId() { - DCHECK(CanPasteAndGo()); - - // Use PASTE_AND_SEARCH as the default fallback (although the DCHECK above - // should never trigger). - if (!model_->is_paste_and_search()) - return IDS_PASTE_AND_GO; - else - return IDS_PASTE_AND_SEARCH; -} - -void AutocompleteEditViewMac::OnPasteAndGo() { - if (CanPasteAndGo()) - model_->PasteAndGo(); -} - -void AutocompleteEditViewMac::OnFrameChanged() { - // TODO(shess): UpdatePopupAppearance() is called frequently, so it - // should be really cheap, but in this case we could probably make - // things even cheaper by refactoring between the popup-placement - // code and the matrix-population code. - popup_view_->UpdatePopupAppearance(); - model_->PopupBoundsChangedTo(popup_view_->GetTargetBounds()); - - // Give controller a chance to rearrange decorations. - model_->OnChanged(); -} - -bool AutocompleteEditViewMac::OnBackspacePressed() { - // Don't intercept if not in keyword search mode. - if (model_->is_keyword_hint() || model_->keyword().empty()) { - return false; - } - - // Don't intercept if there is a selection, or the cursor isn't at - // the leftmost position. - const NSRange selection = GetSelectedRange(); - if (selection.length > 0 || selection.location > 0) { - return false; - } - - // We're showing a keyword and the user pressed backspace at the - // beginning of the text. Delete the selected keyword. - model_->ClearKeyword(GetText()); - return true; -} - -NSRange AutocompleteEditViewMac::SelectionRangeForProposedRange( - NSRange proposed_range) { - // Should never call this function unless editing is in progress. - DCHECK([field_ currentEditor]); - - if (![field_ currentEditor]) - return proposed_range; - - // Do not use [field_ stringValue] here, as that forces a sync between the - // field and the editor. This sync will end up setting the selection, which - // in turn calls this method, leading to an infinite loop. Instead, retrieve - // the current string value directly from the editor. - size_t text_length = [[[field_ currentEditor] string] length]; - - // Cannot select suggested text. - size_t max = text_length - suggest_text_length_; - NSUInteger start = proposed_range.location; - NSUInteger end = proposed_range.location + proposed_range.length; - - if (start > max) - start = max; - - if (end > max) - end = max; - - return NSMakeRange(start, end - start); -} - -void AutocompleteEditViewMac::OnControlKeyChanged(bool pressed) { - model_->OnControlKeyChanged(pressed); -} - -void AutocompleteEditViewMac::FocusLocation(bool select_all) { - if ([field_ isEditable]) { - // If the text field has a field editor, it's the first responder, meaning - // that it's already focused. makeFirstResponder: will select all, so only - // call it if this behavior is desired. - if (select_all || ![field_ currentEditor]) - [[field_ window] makeFirstResponder:field_]; - DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); - } -} - -// TODO(shess): Copied from autocomplete_edit_view_win.cc. Could this -// be pushed into the model? -string16 AutocompleteEditViewMac::GetClipboardText( - ui::Clipboard* clipboard) { - // autocomplete_edit_view_win.cc assumes this can never happen, we - // will too. - DCHECK(clipboard); - - if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), - ui::Clipboard::BUFFER_STANDARD)) { - string16 text16; - clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &text16); - - // Note: Unlike in the find popup and textfield view, here we completely - // remove whitespace strings containing newlines. We assume users are - // most likely pasting in URLs that may have been split into multiple - // lines in terminals, email programs, etc., and so linebreaks indicate - // completely bogus whitespace that would just cause the input to be - // invalid. - return CollapseWhitespace(text16, true); - } - - // Try bookmark format. - // - // It is tempting to try bookmark format first, but the URL we get out of a - // bookmark has been cannonicalized via GURL. This means if a user copies - // and pastes from the URL bar to itself, the text will get fixed up and - // cannonicalized, which is not what the user expects. By pasting in this - // order, we are sure to paste what the user copied. - if (clipboard->IsFormatAvailable(ui::Clipboard::GetUrlWFormatType(), - ui::Clipboard::BUFFER_STANDARD)) { - std::string url_str; - clipboard->ReadBookmark(NULL, &url_str); - // pass resulting url string through GURL to normalize - GURL url(url_str); - if (url.is_valid()) { - return UTF8ToUTF16(url.spec()); - } - } - - return string16(); -} - -// static -NSFont* AutocompleteEditViewMac::GetFieldFont() { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - return rb.GetFont(ResourceBundle::BaseFont).GetNativeFont(); -} - -NSUInteger AutocompleteEditViewMac::GetTextLength() const { - return ([field_ currentEditor] ? - [[[field_ currentEditor] string] length] : - [[field_ stringValue] length]) - suggest_text_length_; -} - -void AutocompleteEditViewMac::PlaceCaretAt(NSUInteger pos) { - DCHECK(pos <= GetTextLength()); - SetSelectedRange(NSMakeRange(pos, pos)); -} - -bool AutocompleteEditViewMac::IsCaretAtEnd() const { - const NSRange selection = GetSelectedRange(); - return selection.length == 0 && selection.location == GetTextLength(); -} |