From 81c2122f16c82b52c19b474775d2a15aa065e77a Mon Sep 17 00:00:00 2001 From: "pkasting@chromium.org" Date: Wed, 10 Sep 2008 19:35:52 +0000 Subject: [PATCH] First pass at splitting the AutocompleteEdit into Model and View. This was noticeably harder than with the Popup and I'm not at all sure I've made the right decisions :(. The View code is about 3x larger than the model. BUG=1343512 Review URL: http://codereview.chromium.org/1872 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2004 0039d316-1c4b-4281-b951-d872f2087c98 --- .../browser/autocomplete/autocomplete_edit.cc | 1706 +++++++++-------- .../browser/autocomplete/autocomplete_edit.h | 873 +++++---- .../autocomplete/autocomplete_popup.cc | 141 +- .../browser/autocomplete/autocomplete_popup.h | 50 +- .../browser/autocomplete/edit_drop_target.cc | 4 +- .../browser/autocomplete/edit_drop_target.h | 10 +- .../automation_autocomplete_edit_tracker.h | 10 +- .../browser/automation/automation_provider.cc | 25 +- chrome/browser/browser_commands.cc | 14 +- chrome/browser/tab_contents.h | 6 +- chrome/browser/views/location_bar_view.cc | 18 +- chrome/browser/views/location_bar_view.h | 14 +- .../autocomplete_accessibility.cc | 2 +- .../autocomplete_accessibility.h | 10 +- 14 files changed, 1576 insertions(+), 1307 deletions(-) diff --git a/chrome/browser/autocomplete/autocomplete_edit.cc b/chrome/browser/autocomplete/autocomplete_edit.cc index 4c06a1ccddb03d..a48d4e8e82afe1 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.cc +++ b/chrome/browser/autocomplete/autocomplete_edit.cc @@ -39,22 +39,570 @@ #pragma comment(lib, "oleacc.lib") // Needed for accessibility support. -// TODO (jcampan): these colors should be derived from the system colors to -// ensure they show properly. Bug #948807. -// Colors used to emphasize the scheme in the URL. -static const COLORREF kSecureSchemeColor = RGB(0, 150, 20); -static const COLORREF kInsecureSchemeColor = RGB(200, 0, 0); +/////////////////////////////////////////////////////////////////////////////// +// AutocompleteEditModel -// Colors used to strike-out the scheme when it is insecure. -static const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0); -static const SkColor kSchemeSelectedStrikeoutColor = - SkColorSetRGB(255, 255, 255); +// A single AutocompleteController used solely for making synchronous calls to +// determine how to deal with the clipboard contents for Paste And Go +// functionality. We avoid using the popup's controller here because we don't +// want to interrupt in-progress queries or modify the popup state just +// because the user right-clicked the edit. We don't need a controller for +// every edit because this will always be accessed on the main thread, so we +// won't have thread-safety problems. +static AutocompleteController* paste_and_go_controller = NULL; +static int paste_and_go_controller_refcount = 0; + +AutocompleteEditModel::AutocompleteEditModel( + AutocompleteEditView* view, + AutocompleteEditController* controller, + Profile* profile) + : view_(view), + controller_(controller), + has_focus_(false), + user_input_in_progress_(false), + just_deleted_text_(false), + has_temporary_text_(false), + paste_state_(NONE), + control_key_state_(UP), + is_keyword_hint_(false), + disable_keyword_ui_(false), + show_search_hint_(true), + profile_(profile) { + if (++paste_and_go_controller_refcount == 1) { + // We don't have a controller yet, so create one. No listener is needed + // since we'll only be doing synchronous calls, and no profile is set since + // we'll set this before each call to the controller. + paste_and_go_controller = new AutocompleteController(NULL, NULL); + } +} + +AutocompleteEditModel::~AutocompleteEditModel() { + if (--paste_and_go_controller_refcount == 0) + delete paste_and_go_controller; +} + +void AutocompleteEditModel::SetProfile(Profile* profile) { + DCHECK(profile); + profile_ = profile; + popup_->SetProfile(profile); +} + +const AutocompleteEditModel::State + AutocompleteEditModel::GetStateForTabSwitch() { + // Like typing, switching tabs "accepts" the temporary text as the user + // text, because it makes little sense to have temporary text when the + // popup is closed. + if (user_input_in_progress_) + InternalSetUserText(UserTextFromDisplayText(view_->GetText())); + + return State(user_input_in_progress_, user_text_, + popup_->manually_selected_match_, keyword_, is_keyword_hint_, + disable_keyword_ui_, show_search_hint_); +} + +void AutocompleteEditModel::RestoreState(const State& state) { + // Restore any user editing. + if (state.user_input_in_progress) { + // NOTE: Be sure and set keyword-related state BEFORE invoking + // DisplayTextFromUserText(), as its result depends upon this state. + keyword_ = state.keyword; + is_keyword_hint_ = state.is_keyword_hint; + disable_keyword_ui_ = state.disable_keyword_ui; + show_search_hint_ = state.show_search_hint; + view_->SetUserText(state.user_text, + DisplayTextFromUserText(state.user_text), false); + popup_->manually_selected_match_ = state.manually_selected_match; + } +} + +bool AutocompleteEditModel::UpdatePermanentText( + const std::wstring& new_permanent_text) { + // When there's a new URL, and the user is not editing anything or the edit + // doesn't have focus, we want to revert the edit to show the new URL. (The + // common case where the edit doesn't have focus is when the user has started + // an edit and then abandoned it and clicked a link on the page.) + const bool visibly_changed_permanent_text = + (permanent_text_ != new_permanent_text) && + (!user_input_in_progress_ || !has_focus_); + + permanent_text_ = new_permanent_text; + return visibly_changed_permanent_text; +} + +void AutocompleteEditModel::SetUserText(const std::wstring& text) { + SetInputInProgress(true); + InternalSetUserText(text); + paste_state_ = NONE; + has_temporary_text_ = false; + popup_->manually_selected_match_.Clear(); +} + +void AutocompleteEditModel::GetDataForURLExport(GURL* url, + std::wstring* title, + SkBitmap* favicon) { + const std::wstring url_str(GetURLForCurrentText(NULL, NULL, NULL)); + *url = GURL(url_str); + if (url_str == permanent_text_) { + *title = controller_->GetTitle(); + *favicon = controller_->GetFavIcon(); + } +} + +std::wstring AutocompleteEditModel::GetDesiredTLD() const { + return (control_key_state_ == DOWN_WITHOUT_CHANGE) ? + std::wstring(L"com") : std::wstring(); +} + +bool AutocompleteEditModel::CurrentTextIsURL() { + // If !user_input_in_progress_, the permanent text is showing, which should + // always be a URL, so no further checking is needed. By avoiding checking in + // this case, we avoid calling into the autocomplete providers, and thus + // initializing the history system, as long as possible, which speeds startup. + if (!user_input_in_progress_) + return true; + + PageTransition::Type transition = PageTransition::LINK; + GetURLForCurrentText(&transition, NULL, NULL); + return transition == PageTransition::TYPED; +} + +bool AutocompleteEditModel::GetURLForText(const std::wstring& text, + GURL* url) const { + url_parse::Parsed parts; + const AutocompleteInput::Type type = AutocompleteInput::Parse( + UserTextFromDisplayText(text), std::wstring(), &parts, NULL); + if (type != AutocompleteInput::URL) + return false; + + *url = GURL(URLFixerUpper::FixupURL(text, std::wstring())); + return true; +} + +void AutocompleteEditModel::SetInputInProgress(bool in_progress) { + if (user_input_in_progress_ == in_progress) + return; + + user_input_in_progress_ = in_progress; + controller_->OnInputInProgress(in_progress); +} + +void AutocompleteEditModel::Revert() { + SetInputInProgress(false); + paste_state_ = NONE; + InternalSetUserText(std::wstring()); + keyword_.clear(); + is_keyword_hint_ = false; + disable_keyword_ui_ = false; + show_search_hint_ = permanent_text_.empty(); + has_temporary_text_ = false; + view_->SetWindowTextAndCaretPos(permanent_text_, + has_focus_ ? permanent_text_.length() : 0); +} + +void AutocompleteEditModel::StartAutocomplete( + bool prevent_inline_autocomplete) const { + popup_->StartAutocomplete(user_text_, GetDesiredTLD(), + prevent_inline_autocomplete || just_deleted_text_ || + (paste_state_ != NONE)); +} + +bool AutocompleteEditModel::CanPasteAndGo(const std::wstring& text) const { + // Reset local state. + paste_and_go_url_.clear(); + paste_and_go_transition_ = PageTransition::TYPED; + paste_and_go_alternate_nav_url_.clear(); + + // See if the clipboard text can be parsed. + const AutocompleteInput input(text, std::wstring(), true); + if (input.type() == AutocompleteInput::INVALID) + return false; + + // Ask the controller what do do with this input. + paste_and_go_controller->SetProfile(profile_); + // This is cheap, and since there's one + // paste_and_go_controller for many tabs which + // may all have different profiles, it ensures + // we're always using the right one. + const bool done = paste_and_go_controller->Start(input, false, true); + DCHECK(done); + AutocompleteResult result; + paste_and_go_controller->GetResult(&result); + if (result.empty()) + return false; + + // Set local state based on the default action for this input. + result.SetDefaultMatch(AutocompleteResult::Selection()); + const AutocompleteResult::const_iterator match(result.default_match()); + DCHECK(match != result.end()); + paste_and_go_url_ = match->destination_url; + paste_and_go_transition_ = match->transition; + paste_and_go_alternate_nav_url_ = result.GetAlternateNavURL(input, match); + + return !paste_and_go_url_.empty(); +} + +void AutocompleteEditModel::PasteAndGo() { + // The final parameter to OpenURL, keyword, is not quite correct here: it's + // possible to "paste and go" a string that contains a keyword. This is + // enough of an edge case that we ignore this possibility. + view_->RevertAll(); + view_->OpenURL(paste_and_go_url_, CURRENT_TAB, paste_and_go_transition_, + paste_and_go_alternate_nav_url_, AutocompletePopupModel::kNoMatch, + std::wstring()); +} + +void AutocompleteEditModel::AcceptInput(WindowOpenDisposition disposition, + bool for_drop) { + // Get the URL and transition type for the selected entry. + PageTransition::Type transition; + bool is_history_what_you_typed_match; + std::wstring alternate_nav_url; + const std::wstring url(GetURLForCurrentText(&transition, + &is_history_what_you_typed_match, + &alternate_nav_url)); + if (url.empty()) + return; + + if (url == permanent_text_) { + // When the user hit enter on the existing permanent URL, treat it like a + // reload for scoring purposes. We could detect this by just checking + // user_input_in_progress_, but it seems better to treat "edits" that end + // up leaving the URL unchanged (e.g. deleting the last character and then + // retyping it) as reloads too. + transition = PageTransition::RELOAD; + } else if (for_drop || ((paste_state_ != NONE) && + is_history_what_you_typed_match)) { + // When the user pasted in a URL and hit enter, score it like a link click + // rather than a normal typed URL, so it doesn't get inline autocompleted + // as aggressively later. + transition = PageTransition::LINK; + } + + view_->OpenURL(url, disposition, transition, alternate_nav_url, + AutocompletePopupModel::kNoMatch, + is_keyword_hint_ ? std::wstring() : keyword_); +} + +void AutocompleteEditModel::SendOpenNotification(size_t selected_line, + const std::wstring& keyword) { + // We only care about cases where there is a selection (i.e. the popup is + // open). + if (popup_->is_open()) { + scoped_ptr log(popup_->GetAutocompleteLog()); + if (selected_line != AutocompletePopupModel::kNoMatch) + log->selected_index = selected_line; + else if (!has_temporary_text_) + log->inline_autocompleted_length = inline_autocomplete_text_.length(); + NotificationService::current()->Notify( + NOTIFY_OMNIBOX_OPENED_URL, Source(profile_), + Details(log.get())); + } + + TemplateURLModel* template_url_model = profile_->GetTemplateURLModel(); + if (keyword.empty() || !template_url_model) + return; + + const TemplateURL* const template_url = + template_url_model->GetTemplateURLForKeyword(keyword); + if (template_url) { + UserMetrics::RecordAction(L"AcceptedKeyword", profile_); + template_url_model->IncrementUsageCount(template_url); + } + + // NOTE: We purposefully don't increment the usage count of the default search + // engine, if applicable; see comments in template_url.h. +} + +void AutocompleteEditModel::AcceptKeyword() { + view_->OnBeforePossibleChange(); + view_->SetWindowText(L""); + popup_->manually_selected_match_.Clear(); + popup_->manually_selected_match_.provider_affinity = + popup_->autocomplete_controller()->keyword_provider(); + is_keyword_hint_ = false; + disable_keyword_ui_ = false; + view_->OnAfterPossibleChange(); + just_deleted_text_ = false; // OnAfterPossibleChange() erroneously sets this + // since the edit contents have disappeared. It + // doesn't really matter, but we clear it to be + // consistent. + UserMetrics::RecordAction(L"AcceptedKeywordHint", profile_); +} + +void AutocompleteEditModel::ClearKeyword(const std::wstring& visible_text) { + view_->OnBeforePossibleChange(); + const std::wstring window_text(keyword_ + visible_text); + view_->SetWindowTextAndCaretPos(window_text.c_str(), keyword_.length()); + popup_->manually_selected_match_.Clear(); + keyword_.clear(); + view_->OnAfterPossibleChange(); + just_deleted_text_ = true; // OnAfterPossibleChange() fails to clear this + // since the edit contents have actually grown + // longer. +} + +bool AutocompleteEditModel::query_in_progress() const { + return popup_->query_in_progress(); +} + +const AutocompleteResult* AutocompleteEditModel::latest_result() const { + return popup_->latest_result(); +} + +void AutocompleteEditModel::OnSetFocus(bool control_down) { + has_focus_ = true; + control_key_state_ = control_down ? DOWN_WITHOUT_CHANGE : UP; +} + +void AutocompleteEditModel::OnKillFocus() { + has_focus_ = false; + control_key_state_ = UP; + paste_state_ = NONE; + + // Like typing, killing focus "accepts" the temporary text as the user + // text, because it makes little sense to have temporary text when the + // popup is closed. + InternalSetUserText(UserTextFromDisplayText(view_->GetText())); + has_temporary_text_ = false; +} + +bool AutocompleteEditModel::OnEscapeKeyPressed() { + // Only do something when there is input in progress -- otherwise, if focus + // happens to be in the location bar, users can't still hit to stop a + // load. + if (!user_input_in_progress_) + return false; + + if (!has_temporary_text_ || + (popup_->URLsForCurrentSelection(NULL, NULL, NULL) == original_url_)) { + // The popup isn't open or the selection in it is still the default + // selection, so revert the box all the way back to its unedited state. + view_->RevertAll(); + return true; + } + + // The user typed something, then selected a different item. Restore the + // text they typed and change back to the default item. + // NOTE: This purposefully does not reset paste_state_. + just_deleted_text_ = false; + has_temporary_text_ = false; + popup_->manually_selected_match_ = original_selected_match_; + view_->OnRevertTemporaryText(user_text_ + inline_autocomplete_text_); + return true; +} + +void AutocompleteEditModel::OnControlKeyChanged(bool pressed) { + // Don't change anything unless the key state is actually toggling. + if (pressed == (control_key_state_ == UP)) { + control_key_state_ = pressed ? DOWN_WITHOUT_CHANGE : UP; + if (popup_->is_open()) { + // Autocomplete history provider results may change, so refresh the + // popup. This will force user_input_in_progress_ to true, but if the + // popup is open, that should have already been the case. + view_->UpdatePopup(); + } + } +} + +void AutocompleteEditModel::OnUpOrDownKeyPressed(int count) { + // NOTE: This purposefully don't trigger any code that resets paste_state_. + disable_keyword_ui_ = false; + if (!popup_->is_open()) { + if (!popup_->query_in_progress()) { + // The popup is neither open nor working on a query already. So, start an + // autocomplete query for the current text. This also sets + // user_input_in_progress_ to true, which we want: if the user has started + // to interact with the popup, changing the permanent_text_ shouldn't + // change the displayed text. + // Note: This does not force the popup to open immediately. + if (!user_input_in_progress_) + InternalSetUserText(permanent_text_); + view_->UpdatePopup(); + } + + // Now go ahead and force the popup to open, and copy the text of the + // default item into the edit. We ignore |count|, since without the popup + // open, the user doesn't really know what they're interacting with. Since + // the user hit an arrow key to explicitly open the popup, we assume that + // they prefer the temporary text of the default item to their own text, + // like we do when they arrow around an already-open popup. In many cases + // the existing text in the edit and the new text will be the same, and the + // only visible effect will be to cancel any selection and place the cursor + // at the end of the edit. + popup_->Move(0); + } else { + // The popup is open, so the user should be able to interact with it + // normally. + popup_->Move(count); + } +} + +void AutocompleteEditModel::OnPopupDataChanged( + const std::wstring& text, + bool is_temporary_text, + const AutocompleteResult::Selection& previous_selected_match, + const std::wstring& keyword, + bool is_keyword_hint, + bool can_show_search_hint) { + // We don't want to show the search hint if we're showing a keyword hint or + // selected keyword, or (subtle!) if we would be showing a selected keyword + // but for disable_keyword_ui_. + can_show_search_hint &= keyword.empty(); + + // Update keyword/hint-related local state. + bool keyword_state_changed = (keyword_ != keyword) || + ((is_keyword_hint_ != is_keyword_hint) && !keyword.empty()) || + (show_search_hint_ != can_show_search_hint); + if (keyword_state_changed) { + keyword_ = keyword; + is_keyword_hint_ = is_keyword_hint; + show_search_hint_ = can_show_search_hint; + } + + // Handle changes to temporary text. + if (is_temporary_text) { + const bool save_original_selection = !has_temporary_text_; + if (save_original_selection) { + // Save the original selection and URL so it can be reverted later. + has_temporary_text_ = true; + original_url_ = popup_->URLsForCurrentSelection(NULL, NULL, NULL); + original_selected_match_ = previous_selected_match; + } + view_->OnTemporaryTextMaybeChanged(DisplayTextFromUserText(text), + save_original_selection); + return; + } + + // Handle changes to inline autocomplete text. Don't make changes if the user + // is showing temporary text. Making display changes would be obviously + // wrong; making changes to the inline_autocomplete_text_ itself turns out to + // be more subtlely wrong, because it means hitting esc will no longer revert + // to the original state before arrowing. + if (!has_temporary_text_) { + inline_autocomplete_text_ = text; + if (view_->OnInlineAutocompleteTextMaybeChanged( + DisplayTextFromUserText(user_text_ + inline_autocomplete_text_), + DisplayTextFromUserText(user_text_).length())) + return; + } + + // If the above changes didn't warrant a text update but we did change keyword + // state, we have yet to notify the controller about it. + if (keyword_state_changed) + controller_->OnChanged(); +} + +bool AutocompleteEditModel::OnAfterPossibleChange(const std::wstring& new_text, + bool selection_differs, + bool select_all_before_change, + bool text_differs, + bool just_deleted_text, + bool at_end_of_edit) { + // Update the paste state as appropriate: if we're just finishing a paste + // that replaced all the text, preserve that information; otherwise, if we've + // made some other edit, clear paste tracking. + if (paste_state_ == REPLACING_ALL) + paste_state_ = REPLACED_ALL; + else if (text_differs) + paste_state_ = NONE; + + // If something has changed while the control key is down, prevent + // "ctrl-enter" until the control key is released. When we do this, we need + // to update the popup if it's open, since the desired_tld will have changed. + if ((text_differs || selection_differs) && + (control_key_state_ == DOWN_WITHOUT_CHANGE)) { + control_key_state_ = DOWN_WITH_CHANGE; + if (!text_differs && !popup_->is_open()) + return false; // Don't open the popup for no reason. + } else if (!text_differs && + (inline_autocomplete_text_.empty() || !selection_differs)) { + return false; + } + + const bool had_keyword = !is_keyword_hint_ && !keyword_.empty(); + + // Modifying the selection counts as accepting the autocompleted text. + InternalSetUserText(UserTextFromDisplayText(new_text)); + has_temporary_text_ = false; + + if (text_differs) { + // When the user has deleted text, don't allow inline autocomplete. + just_deleted_text_ = just_deleted_text; + + // When the user doesn't have a selected keyword, deleting text or replacing + // all of it with something else should reset the provider affinity. The + // typical use case for deleting is that the user starts typing, sees that + // some entry is close to what he wants, arrows to it, and then deletes some + // unnecessary bit from the end of the string. In this case the user didn't + // actually want "provider X", he wanted the string from that entry for + // editing purposes, and he's no longer looking at the popup to notice that, + // despite deleting some text, the action we'll take on enter hasn't changed + // at all. + if (!had_keyword && (just_deleted_text_ || select_all_before_change)) { + popup_->manually_selected_match_.Clear(); + } + } + + // Disable the fancy keyword UI if the user didn't already have a visible + // keyword and is not at the end of the edit. This prevents us from showing + // the fancy UI (and interrupting the user's editing) if the user happens to + // have a keyword for 'a', types 'ab' then puts a space between the 'a' and + // the 'b'. + disable_keyword_ui_ = (is_keyword_hint_ || keyword_.empty()) && + !at_end_of_edit; + + view_->UpdatePopup(); + + if (!had_keyword && !is_keyword_hint_ && !keyword_.empty()) { + // Went from no selected keyword to a selected keyword. Set the affinity to + // the keyword provider. This forces the selected keyword to persist even + // if the user deletes all the text. + popup_->manually_selected_match_.Clear(); + popup_->manually_selected_match_.provider_affinity = + popup_->autocomplete_controller()->keyword_provider(); + } + + return true; +} + +void AutocompleteEditModel::InternalSetUserText(const std::wstring& text) { + user_text_ = text; + just_deleted_text_ = false; + inline_autocomplete_text_.clear(); +} + +std::wstring AutocompleteEditModel::DisplayTextFromUserText( + const std::wstring& text) const { + return (is_keyword_hint_ || keyword_.empty()) ? + text : KeywordProvider::SplitReplacementStringFromInput(text); +} + +std::wstring AutocompleteEditModel::UserTextFromDisplayText( + const std::wstring& text) const { + return (is_keyword_hint_ || keyword_.empty()) ? + text : (keyword_ + L" " + text); +} + +std::wstring AutocompleteEditModel::GetURLForCurrentText( + PageTransition::Type* transition, + bool* is_history_what_you_typed_match, + std::wstring* alternate_nav_url) { + return (popup_->is_open() || popup_->query_in_progress()) ? + popup_->URLsForCurrentSelection(transition, + is_history_what_you_typed_match, + alternate_nav_url) : + popup_->URLsForDefaultMatch(UserTextFromDisplayText(view_->GetText()), + GetDesiredTLD(), transition, + is_history_what_you_typed_match, + alternate_nav_url); +} /////////////////////////////////////////////////////////////////////////////// // Helper classes -AutocompleteEdit::ScopedFreeze::ScopedFreeze(AutocompleteEdit* edit, - ITextDocument* text_object_model) +AutocompleteEditView::ScopedFreeze::ScopedFreeze( + AutocompleteEditView* edit, + ITextDocument* text_object_model) : edit_(edit), text_object_model_(text_object_model) { // Freeze the screen. @@ -64,7 +612,7 @@ AutocompleteEdit::ScopedFreeze::ScopedFreeze(AutocompleteEdit* edit, } } -AutocompleteEdit::ScopedFreeze::~ScopedFreeze() { +AutocompleteEditView::ScopedFreeze::~ScopedFreeze() { // Unfreeze the screen. // NOTE: If this destructor is reached while the edit is being destroyed (for // example, because we double-clicked the edit of a popup and caused it to @@ -83,7 +631,7 @@ AutocompleteEdit::ScopedFreeze::~ScopedFreeze() { } } -AutocompleteEdit::ScopedSuspendUndo::ScopedSuspendUndo( +AutocompleteEditView::ScopedSuspendUndo::ScopedSuspendUndo( ITextDocument* text_object_model) : text_object_model_(text_object_model) { // Suspend Undo processing. @@ -91,71 +639,60 @@ AutocompleteEdit::ScopedSuspendUndo::ScopedSuspendUndo( text_object_model_->Undo(tomSuspend, NULL); } -AutocompleteEdit::ScopedSuspendUndo::~ScopedSuspendUndo() { +AutocompleteEditView::ScopedSuspendUndo::~ScopedSuspendUndo() { // Resume Undo processing. if (text_object_model_) text_object_model_->Undo(tomResume, NULL); } /////////////////////////////////////////////////////////////////////////////// -// AutocompleteEdit +// AutocompleteEditView + +// TODO (jcampan): these colors should be derived from the system colors to +// ensure they show properly. Bug #948807. +// Colors used to emphasize the scheme in the URL. +static const COLORREF kSecureSchemeColor = RGB(0, 150, 20); +static const COLORREF kInsecureSchemeColor = RGB(200, 0, 0); + +// Colors used to strike-out the scheme when it is insecure. +static const SkColor kSchemeStrikeoutColor = SkColorSetRGB(210, 0, 0); +static const SkColor kSchemeSelectedStrikeoutColor = + SkColorSetRGB(255, 255, 255); // These are used to hook the CRichEditCtrl's calls to BeginPaint() and // EndPaint() and provide a memory DC instead. See OnPaint(). static HWND edit_hwnd = NULL; static PAINTSTRUCT paint_struct; -// A single AutocompleteController used solely for making synchronous calls to -// determine how to deal with the clipboard contents for Paste And Go -// functionality. We avoid using the popup's controller here because we don't -// want to interrupt in-progress queries or modify the popup state just -// because the user right-clicked the edit. We don't need a controller for -// every edit because this will always be accessed on the main thread, so we -// won't have thread-safety problems. -static AutocompleteController* paste_and_go_controller = NULL; -static int paste_and_go_controller_refcount = 0; - -AutocompleteEdit::AutocompleteEdit(const ChromeFont& font, - Controller* controller, - ToolbarModel* model, - ChromeViews::View* parent_view, - HWND hwnd, - Profile* profile, - CommandController* command_controller, - bool popup_window_mode) - : controller_(controller), - model_(model), - popup_(new AutocompletePopupModel(font, this, profile)), +AutocompleteEditView::AutocompleteEditView( + const ChromeFont& font, + AutocompleteEditController* controller, + ToolbarModel* toolbar_model, + ChromeViews::View* parent_view, + HWND hwnd, + Profile* profile, + CommandController* command_controller, + bool popup_window_mode) + : model_(new AutocompleteEditModel(this, controller, profile)), + popup_model_(new AutocompletePopupModel(font, this, model_.get(), + profile)), + controller_(controller), + parent_view_(parent_view), + toolbar_model_(toolbar_model), + command_controller_(command_controller), popup_window_mode_(popup_window_mode), - has_focus_(false), - user_input_in_progress_(false), - just_deleted_text_(false), - has_temporary_text_(false), - paste_state_(NONE), tracking_click_(false), tracking_double_click_(false), double_click_time_(0), can_discard_mousemove_(false), - control_key_state_(UP), - command_controller_(command_controller), - parent_view_(parent_view), font_(font), - profile_(profile), possible_drag_(false), in_drag_(false), initiated_drag_(false), - drop_highlight_position_(-1), - is_keyword_hint_(false), - disable_keyword_ui_(false), - show_search_hint_(true), - background_color_(0), - scheme_security_level_(ToolbarModel::NORMAL) { - if (!popup_window_mode_ && ++paste_and_go_controller_refcount == 1) { - // We don't have a controller yet, so create one. No listener is needed - // since we'll only be doing synchronous calls, and no profile is set since - // we'll set this before each call to the controller. - paste_and_go_controller = new AutocompleteController(NULL, NULL); - } + drop_highlight_position_(-1), + background_color_(0), + scheme_security_level_(ToolbarModel::NORMAL) { + model_->set_popup_model(popup_model_.get()); saved_selection_for_focus_change_.cpMin = -1; @@ -256,35 +793,38 @@ AutocompleteEdit::AutocompleteEdit(const ChromeFont& font, } } -AutocompleteEdit::~AutocompleteEdit() { +AutocompleteEditView::~AutocompleteEditView() { NotificationService::current()->Notify(NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, - Source(this), NotificationService::NoDetails()); + Source(this), NotificationService::NoDetails()); +} - if (!popup_window_mode_ && --paste_and_go_controller_refcount == 0) - delete paste_and_go_controller; +void AutocompleteEditView::SaveStateToTab(TabContents* tab) { + DCHECK(tab); + + const AutocompleteEditModel::State model_state( + model_->GetStateForTabSwitch()); + + CHARRANGE selection; + GetSelection(selection); + tab->set_saved_location_bar_state(new AutocompleteEditState(model_state, + State(selection, saved_selection_for_focus_change_))); } -void AutocompleteEdit::Update(const TabContents* tab_for_state_restoring) { - // When there's a new URL, and the user is not editing anything or the edit - // doesn't have focus, we want to revert the edit to show the new URL. (The - // common case where the edit doesn't have focus is when the user has started - // an edit and then abandoned it and clicked a link on the page.) - std::wstring permanent_text = model_->GetText(); +void AutocompleteEditView::Update(const TabContents* tab_for_state_restoring) { const bool visibly_changed_permanent_text = - (permanent_text_ != permanent_text) && - (!user_input_in_progress_ || !has_focus_); - - permanent_text_ = permanent_text; + model_->UpdatePermanentText(toolbar_model_->GetText()); - COLORREF background_color = - LocationBarView::kBackgroundColorByLevel[model_->GetSchemeSecurityLevel()]; + const ToolbarModel::SecurityLevel security_level = + toolbar_model_->GetSchemeSecurityLevel(); + const COLORREF background_color = + LocationBarView::kBackgroundColorByLevel[security_level]; + const bool changed_security_level = + (security_level != scheme_security_level_); // Bail early when no visible state will actually change (prevents an // unnecessary ScopedFreeze, and thus UpdateWindow()). - if ((background_color == background_color_) && - (model_->GetSchemeSecurityLevel() == scheme_security_level_) && - !visibly_changed_permanent_text && - !tab_for_state_restoring) + if ((background_color == background_color_) && !changed_security_level && + !visibly_changed_permanent_text && !tab_for_state_restoring) return; // Update our local state as desired. We set scheme_security_level_ here so @@ -295,9 +835,7 @@ void AutocompleteEdit::Update(const TabContents* tab_for_state_restoring) { background_color_ = background_color; SetBackgroundColor(background_color_); } - const bool changed_security_level = - (model_->GetSchemeSecurityLevel() != scheme_security_level_); - scheme_security_level_ = model_->GetSchemeSecurityLevel(); + scheme_security_level_ = security_level; // When we're switching to a new tab, restore its state, if any. if (tab_for_state_restoring) { @@ -306,27 +844,16 @@ void AutocompleteEdit::Update(const TabContents* tab_for_state_restoring) { // won't overwrite all our local state. RevertAll(); - const AutocompleteEdit::State* const state = + const AutocompleteEditState* const state = tab_for_state_restoring->saved_location_bar_state(); if (state) { - // Restore any user editing. - if (state->user_input_in_progress) { - // NOTE: Be sure and set keyword-related state BEFORE invoking - // DisplayTextFromUserText(), as its result depends upon this state. - keyword_ = state->keyword; - is_keyword_hint_ = state->is_keyword_hint; - disable_keyword_ui_ = state->disable_keyword_ui; - show_search_hint_ = state->show_search_hint; - SetUserText(state->user_text, DisplayTextFromUserText(state->user_text), - false); - popup_->manually_selected_match_ = state->manually_selected_match; - } + model_->RestoreState(state->model_state); // Restore user's selection. We do this after restoring the user_text // above so we're selecting in the correct string. - SetSelectionRange(state->selection); + SetSelectionRange(state->view_state.selection); saved_selection_for_focus_change_ = - state->saved_selection_for_focus_change; + state->view_state.saved_selection_for_focus_change; } } else if (visibly_changed_permanent_text) { // Not switching tabs, just updating the permanent text. (In the case where @@ -358,138 +885,103 @@ void AutocompleteEdit::Update(const TabContents* tab_for_state_restoring) { } } -void AutocompleteEdit::SetProfile(Profile* profile) { - DCHECK(profile); - profile_ = profile; - popup_->SetProfile(profile); -} - -void AutocompleteEdit::SaveStateToTab(TabContents* tab) { - DCHECK(tab); +void AutocompleteEditView::OpenURL(const std::wstring& url, + WindowOpenDisposition disposition, + PageTransition::Type transition, + const std::wstring& alternate_nav_url, + size_t selected_line, + const std::wstring& keyword) { + if (url.empty()) + return; - // Like typing, switching tabs "accepts" the temporary text as the user - // text, because it makes little sense to have temporary text when the - // popup is closed. - if (user_input_in_progress_) - InternalSetUserText(UserTextFromDisplayText(GetText())); + model_->SendOpenNotification(selected_line, keyword); - CHARRANGE selection; - GetSelection(selection); - tab->set_saved_location_bar_state(new State(selection, - saved_selection_for_focus_change_, user_input_in_progress_, user_text_, - popup_->manually_selected_match_, keyword_, is_keyword_hint_, - disable_keyword_ui_, show_search_hint_)); + ScopedFreeze freeze(this, GetTextObjectModel()); + if (disposition != NEW_BACKGROUND_TAB) + RevertAll(); // Revert the box to its unedited state + controller_->OnAutocompleteAccept(url, disposition, transition, + alternate_nav_url); } -std::wstring AutocompleteEdit::GetText() const { +std::wstring AutocompleteEditView::GetText() const { const int len = GetTextLength() + 1; std::wstring str; GetWindowText(WriteInto(&str, len), len); return str; } -std::wstring AutocompleteEdit::GetURLForCurrentText( - PageTransition::Type* transition, - bool* is_history_what_you_typed_match, - std::wstring* alternate_nav_url) { - return (popup_->is_open() || popup_->query_in_progress()) ? - popup_->URLsForCurrentSelection(transition, - is_history_what_you_typed_match, - alternate_nav_url) : - popup_->URLsForDefaultMatch(UserTextFromDisplayText(GetText()), - GetDesiredTLD(), transition, - is_history_what_you_typed_match, - alternate_nav_url); +void AutocompleteEditView::SetUserText(const std::wstring& text, + const std::wstring& display_text, + bool update_popup) { + ScopedFreeze freeze(this, GetTextObjectModel()); + model_->SetUserText(text); + saved_selection_for_focus_change_.cpMin = -1; + SetWindowTextAndCaretPos(display_text, display_text.length()); + if (update_popup) + UpdatePopup(); + TextChanged(); +} + +void AutocompleteEditView::SetWindowTextAndCaretPos(const std::wstring& text, + size_t caret_pos) { + SetWindowText(text.c_str()); + PlaceCaretAt(caret_pos); } -void AutocompleteEdit::SelectAll(bool reversed) { +void AutocompleteEditView::SelectAll(bool reversed) { if (reversed) SetSelection(GetTextLength(), 0); else SetSelection(0, GetTextLength()); } -void AutocompleteEdit::RevertAll() { +void AutocompleteEditView::RevertAll() { ScopedFreeze freeze(this, GetTextObjectModel()); ClosePopup(); - popup_->manually_selected_match_.Clear(); - SetInputInProgress(false); - paste_state_ = NONE; - InternalSetUserText(std::wstring()); - SetWindowText(permanent_text_.c_str()); - keyword_.clear(); - is_keyword_hint_ = false; - disable_keyword_ui_ = false; - show_search_hint_ = permanent_text_.empty(); - PlaceCaretAt(has_focus_ ? permanent_text_.length() : 0); + model_->Revert(); saved_selection_for_focus_change_.cpMin = -1; - has_temporary_text_ = false; TextChanged(); } -void AutocompleteEdit::AcceptInput(WindowOpenDisposition disposition, - bool for_drop) { - // Get the URL and transition type for the selected entry. - PageTransition::Type transition; - bool is_history_what_you_typed_match; - std::wstring alternate_nav_url; - const std::wstring url(GetURLForCurrentText(&transition, - &is_history_what_you_typed_match, - &alternate_nav_url)); - if (url.empty()) - return; - - if (url == permanent_text_) { - // When the user hit enter on the existing permanent URL, treat it like a - // reload for scoring purposes. We could detect this by just checking - // user_input_in_progress_, but it seems better to treat "edits" that end - // up leaving the URL unchanged (e.g. deleting the last character and then - // retyping it) as reloads too. - transition = PageTransition::RELOAD; - } else if (for_drop || ((paste_state_ != NONE) && - is_history_what_you_typed_match)) { - // When the user pasted in a URL and hit enter, score it like a link click - // rather than a normal typed URL, so it doesn't get inline autocompleted - // as aggressively later. - transition = PageTransition::LINK; - } - - OpenURL(url, disposition, transition, alternate_nav_url, - AutocompletePopupModel::kNoMatch, - is_keyword_hint_ ? std::wstring() : keyword_); -} - -void AutocompleteEdit::OpenURL(const std::wstring& url, - WindowOpenDisposition disposition, - PageTransition::Type transition, - const std::wstring& alternate_nav_url, - size_t selected_line, - const std::wstring& keyword) { - if (url.empty()) - return; - +void AutocompleteEditView::UpdatePopup() { ScopedFreeze freeze(this, GetTextObjectModel()); - SendOpenNotification(selected_line, keyword); + model_->SetInputInProgress(true); - if (disposition != NEW_BACKGROUND_TAB) - RevertAll(); // Revert the box to its unedited state - controller_->OnAutocompleteAccept(url, disposition, transition, - alternate_nav_url); -} + if (!model_->has_focus()) { + // When we're in the midst of losing focus, don't rerun autocomplete. This + // can happen when losing focus causes the IME to cancel/finalize a + // composition. We still want to note that user input is in progress, we + // just don't want to do anything else. + // + // Note that in this case the ScopedFreeze above was unnecessary; however, + // we're inside the callstack of OnKillFocus(), which has already frozen the + // edit, so this will never result in an unnecessary UpdateWindow() call. + return; + } -void AutocompleteEdit::ClosePopup() { - popup_->StopAutocomplete(); -} + // Figure out whether the user is trying to compose something in an IME. + bool ime_composing = false; + HIMC context = ImmGetContext(m_hWnd); + if (context) { + ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0); + ImmReleaseContext(m_hWnd, context); + } -bool AutocompleteEdit::query_in_progress() const { - return popup_->query_in_progress(); + // 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 + CHARRANGE sel; + GetSel(sel); + model_->StartAutocomplete((sel.cpMax < GetTextLength()) || ime_composing); } -const AutocompleteResult* AutocompleteEdit::latest_result() const { - return popup_->latest_result(); +void AutocompleteEditView::ClosePopup() { + popup_model_->StopAutocomplete(); } -IAccessible* AutocompleteEdit::GetIAccessible() { +IAccessible* AutocompleteEditView::GetIAccessible() { if (!autocomplete_accessibility_) { CComObject* accessibility = NULL; if (!SUCCEEDED(CComObject::CreateInstance( @@ -510,7 +1002,7 @@ IAccessible* AutocompleteEdit::GetIAccessible() { return autocomplete_accessibility_.Detach(); } -void AutocompleteEdit::SetDropHighlightPosition(int position) { +void AutocompleteEditView::SetDropHighlightPosition(int position) { if (drop_highlight_position_ != position) { RepaintDropHighlight(drop_highlight_position_); drop_highlight_position_ = position; @@ -518,7 +1010,7 @@ void AutocompleteEdit::SetDropHighlightPosition(int position) { } } -void AutocompleteEdit::MoveSelectedText(int new_position) { +void AutocompleteEditView::MoveSelectedText(int new_position) { const std::wstring selected_text(GetSelectedText()); CHARRANGE sel; GetSel(sel); @@ -540,7 +1032,7 @@ void AutocompleteEdit::MoveSelectedText(int new_position) { OnAfterPossibleChange(); } -void AutocompleteEdit::InsertText(int position, const std::wstring& text) { +void AutocompleteEditView::InsertText(int position, const std::wstring& text) { DCHECK((position >= 0) && (position <= GetTextLength())); ScopedFreeze freeze(this, GetTextObjectModel()); OnBeforePossibleChange(); @@ -549,138 +1041,132 @@ void AutocompleteEdit::InsertText(int position, const std::wstring& text) { OnAfterPossibleChange(); } -void AutocompleteEdit::PasteAndGo(const std::wstring& text) { - if (CanPasteAndGo(text)) - PasteAndGo(); +void AutocompleteEditView::OnTemporaryTextMaybeChanged( + const std::wstring& display_text, + bool save_original_selection) { + if (save_original_selection) + GetSelection(original_selection_); + + // Set new text and cursor position. Sometimes this does extra work (e.g. + // when the new text and the old text are identical), but it's only called + // when the user manually changes the selected line in the popup, so that's + // not really a problem. Also, even when the text hasn't changed we'd want to + // update the caret, because if the user had the cursor in the middle of the + // text and then arrowed to another entry with the same text, we'd still want + // to move the caret. + ScopedFreeze freeze(this, GetTextObjectModel()); + SetWindowTextAndCaretPos(display_text, display_text.length()); + TextChanged(); } -bool AutocompleteEdit::OverrideAccelerator( - const ChromeViews::Accelerator& accelerator) { - // Only override , and only when there is input in progress -- otherwise, - // if focus happens to be in the location bar, users can't still hit to - // stop a load. - if ((accelerator.GetKeyCode() != VK_ESCAPE) || accelerator.IsAltDown() || - !user_input_in_progress_) +bool AutocompleteEditView::OnInlineAutocompleteTextMaybeChanged( + const std::wstring& display_text, + size_t user_text_length) { + // Update the text and selection. Because this can be called repeatedly while + // typing, we've careful not to freeze the edit unless we really need to. + // Also, unlike in the temporary text case above, here we don't want to update + // the caret/selection unless we have to, since this might make the user's + // caret position change without warning during typing. + if (display_text == GetText()) return false; - if (!has_temporary_text_ || - (popup_->URLsForCurrentSelection(NULL, NULL, NULL) == original_url_)) { - // The popup isn't open or the selection in it is still the default - // selection, so revert the box all the way back to its unedited state. - RevertAll(); - return true; - } + ScopedFreeze freeze(this, GetTextObjectModel()); + SetWindowText(display_text.c_str()); + // Set a reversed selection to keep the caret in the same position, which + // avoids scrolling the user's text. + SetSelection(static_cast(display_text.length()), + static_cast(user_text_length)); + TextChanged(); + return true; +} - // The user typed something, then selected a different item. Restore the - // text they typed and change back to the default item. - // NOTE: This purposefully does not reset paste_state_. +void AutocompleteEditView::OnRevertTemporaryText(const std::wstring& text) { ScopedFreeze freeze(this, GetTextObjectModel()); - just_deleted_text_ = false; - const std::wstring new_window_text(user_text_ + - inline_autocomplete_text_); - SetWindowText(new_window_text.c_str()); + SetWindowText(text.c_str()); SetSelectionRange(original_selection_); - has_temporary_text_ = false; - popup_->manually_selected_match_ = original_selected_match_; UpdatePopup(); TextChanged(); - return true; } -void AutocompleteEdit::HandleExternalMsg(UINT msg, - UINT flags, - const CPoint& screen_point) { - if (msg == WM_CAPTURECHANGED) { - SendMessage(msg, 0, NULL); - return; +void AutocompleteEditView::OnBeforePossibleChange() { + // Record our state. + text_before_change_ = GetText(); + GetSelection(sel_before_change_); + select_all_before_change_ = IsSelectAll(sel_before_change_); +} + +bool AutocompleteEditView::OnAfterPossibleChange() { + // Prevent the user from selecting the "phantom newline" at the end of the + // edit. If they try, we just silently move the end of the selection back to + // the end of the real text. + CHARRANGE new_sel; + GetSelection(new_sel); + const int length = GetTextLength(); + if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) { + if (new_sel.cpMin > length) + new_sel.cpMin = length; + if (new_sel.cpMax > length) + new_sel.cpMax = length; + SetSelectionRange(new_sel); } + const bool selection_differs = (new_sel.cpMin != sel_before_change_.cpMin) || + (new_sel.cpMax != sel_before_change_.cpMax); + const bool at_end_of_edit = + (new_sel.cpMin == length) && (new_sel.cpMax == length); - CPoint client_point(screen_point); - ::MapWindowPoints(NULL, m_hWnd, &client_point, 1); - SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y)); -} + // See if the text or selection have changed since OnBeforePossibleChange(). + const std::wstring new_text(GetText()); + const bool text_differs = (new_text != text_before_change_); -void AutocompleteEdit::OnPopupDataChanged( - const std::wstring& text, - bool is_temporary_text, - const AutocompleteResult::Selection& previous_selected_match, - const std::wstring& keyword, - bool is_keyword_hint, - bool can_show_search_hint) { - // We don't want to show the search hint if we're showing a keyword hint or - // selected keyword, or (subtle!) if we would be showing a selected keyword - // but for disable_keyword_ui_. - can_show_search_hint &= keyword.empty(); + // When the user has deleted text, we don't allow inline autocomplete. Make + // sure to not flag cases like selecting part of the text and then pasting + // (or typing) the prefix of that selection. (We detect these by making + // sure the caret, which should be after any insertion, hasn't moved + // forward of the old selection start.) + const bool just_deleted_text = + (text_before_change_.length() > new_text.length()) && + (new_sel.cpMin <= std::min(sel_before_change_.cpMin, + sel_before_change_.cpMax)); - // Update keyword/hint-related local state. - bool keyword_state_changed = (keyword_ != keyword) || - ((is_keyword_hint_ != is_keyword_hint) && !keyword.empty()) || - (show_search_hint_ != can_show_search_hint); - if (keyword_state_changed) { - keyword_ = keyword; - is_keyword_hint_ = is_keyword_hint; - show_search_hint_ = can_show_search_hint; - } - // Handle changes to temporary text. - if (is_temporary_text) { - if (!has_temporary_text_) { - // Save the original selection and URL so it can be reverted later. - has_temporary_text_ = true; - GetSelection(original_selection_); - original_url_ = popup_->URLsForCurrentSelection(NULL, NULL, NULL); - original_selected_match_ = previous_selected_match; - } + const bool something_changed = model_->OnAfterPossibleChange(new_text, + selection_differs, select_all_before_change_, text_differs, + just_deleted_text, at_end_of_edit); - // Set new text and cursor position. Sometimes this does extra work (e.g. - // when the new text and the old text are identical), but it's only called - // when the user manually changes the selected line in the popup, so that's - // not really a problem. Also, even when the text hasn't changed we'd want - // to update the caret, because if the user had the cursor in the middle of - // the text and then arrowed to another entry with the same text, we'd still - // want to move the caret. - const std::wstring display_text(DisplayTextFromUserText(text)); - ScopedFreeze freeze(this, GetTextObjectModel()); - SetWindowText(display_text.c_str()); - PlaceCaretAt(display_text.length()); + if (something_changed && text_differs) TextChanged(); - return; - } - // Handle changes to inline autocomplete text. Don't make changes if the user - // is showing temporary text. Making display changes would be obviously - // wrong; making changes to the inline_autocomplete_text_ itself turns out to - // be more subtlely wrong, because it means hitting esc will no longer revert - // to the original state before arrowing. - if (!has_temporary_text_) { - inline_autocomplete_text_ = text; - // Update the text and selection. Because this can be called repeatedly - // while typing, we've careful not to freeze the edit unless we really need - // to. Also, unlike in the temporary text case above, here we don't want to - // update the caret/selection unless we have to, since this might make the - // user's caret position change without warning during typing. - const std::wstring display_text( - DisplayTextFromUserText(user_text_ + inline_autocomplete_text_)); - if (display_text != GetText()) { - ScopedFreeze freeze(this, GetTextObjectModel()); - SetWindowText(display_text.c_str()); - // Set a reversed selection to keep the caret in the same position, which - // avoids scrolling the user's text. - SetSelection( - static_cast(display_text.length()), - static_cast(DisplayTextFromUserText(user_text_).length())); - TextChanged(); - return; - } + return something_changed; +} + +void AutocompleteEditView::PasteAndGo(const std::wstring& text) { + if (CanPasteAndGo(text)) + model_->PasteAndGo(); +} + +bool AutocompleteEditView::OverrideAccelerator( + const ChromeViews::Accelerator& accelerator) { + // Only override . + if ((accelerator.GetKeyCode() != VK_ESCAPE) || accelerator.IsAltDown()) + return false; + + return model_->OnEscapeKeyPressed(); +} + +void AutocompleteEditView::HandleExternalMsg(UINT msg, + UINT flags, + const CPoint& screen_point) { + if (msg == WM_CAPTURECHANGED) { + SendMessage(msg, 0, NULL); + return; } - // If the above changes didn't warrant a text update but we did change keyword - // state, we have yet to notify the controller about it. - if (keyword_state_changed) - controller_->OnChanged(); + CPoint client_point(screen_point); + ::MapWindowPoints(NULL, m_hWnd, &client_point, 1); + SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y)); } -bool AutocompleteEdit::IsCommandEnabled(int id) const { +bool AutocompleteEditView::IsCommandEnabled(int id) const { switch (id) { case IDS_UNDO: return !!CanUndo(); case IDS_CUT: return !!CanCut(); @@ -694,23 +1180,23 @@ bool AutocompleteEdit::IsCommandEnabled(int id) const { } } -bool AutocompleteEdit::GetContextualLabel(int id, std::wstring* out) const { +bool AutocompleteEditView::GetContextualLabel(int id, std::wstring* out) const { if ((id != IDS_PASTE_AND_GO) || - // No need to change the default IDS_PASTE_AND_GO label for a typed - // destination (this is also the type set when Paste And Go is disabled). - (paste_and_go_transition_ == PageTransition::TYPED)) + // No need to change the default IDS_PASTE_AND_GO label unless this is a + // search. + !model_->is_paste_and_search()) return false; out->assign(l10n_util::GetString(IDS_PASTE_AND_SEARCH)); return true; } -void AutocompleteEdit::ExecuteCommand(int id) { +void AutocompleteEditView::ExecuteCommand(int id) { ScopedFreeze freeze(this, GetTextObjectModel()); if (id == IDS_PASTE_AND_GO) { // This case is separate from the switch() below since we don't want to wrap // it in OnBefore/AfterPossibleChange() calls. - PasteAndGo(); + model_->PasteAndGo(); return; } @@ -748,10 +1234,10 @@ void AutocompleteEdit::ExecuteCommand(int id) { } // static -int CALLBACK AutocompleteEdit::WordBreakProc(LPTSTR edit_text, - int current_pos, - int num_bytes, - int action) { +int CALLBACK AutocompleteEditView::WordBreakProc(LPTSTR edit_text, + int current_pos, + int num_bytes, + int action) { // TODO(pkasting): http://b/1111308 We should let other people, like ICU and // GURL, do the work for us here instead of writing all this ourselves. @@ -866,9 +1352,9 @@ int CALLBACK AutocompleteEdit::WordBreakProc(LPTSTR edit_text, } // static -bool AutocompleteEdit::SchemeEnd(LPTSTR edit_text, - int current_pos, - int length) { +bool AutocompleteEditView::SchemeEnd(LPTSTR edit_text, + int current_pos, + int length) { return (current_pos >= 0) && ((length - current_pos) > 2) && (edit_text[current_pos] == ':') && @@ -877,7 +1363,8 @@ bool AutocompleteEdit::SchemeEnd(LPTSTR edit_text, } // static -HDC AutocompleteEdit::BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) { +HDC AutocompleteEditView::BeginPaintIntercept(HWND hWnd, + LPPAINTSTRUCT lpPaint) { if (!edit_hwnd || (hWnd != edit_hwnd)) return ::BeginPaint(hWnd, lpPaint); @@ -886,13 +1373,13 @@ HDC AutocompleteEdit::BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) { } // static -BOOL AutocompleteEdit::EndPaintIntercept(HWND hWnd, - CONST PAINTSTRUCT* lpPaint) { +BOOL AutocompleteEditView::EndPaintIntercept(HWND hWnd, + const PAINTSTRUCT* lpPaint) { return (edit_hwnd && (hWnd == edit_hwnd)) ? true : ::EndPaint(hWnd, lpPaint); } -void AutocompleteEdit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { +void AutocompleteEditView::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { // Don't let alt-enter beep. Not sure this is necessary, as the standard // alt-enter will hit DiscardWMSysChar() and get thrown away, and // ctrl-alt-enter doesn't seem to reach here for some reason? At least not on @@ -913,7 +1400,7 @@ void AutocompleteEdit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags); } -void AutocompleteEdit::OnContextMenu(HWND window, const CPoint& point) { +void AutocompleteEditView::OnContextMenu(HWND window, const CPoint& point) { if (point.x == -1 || point.y == -1) { POINT p; GetCaretPos(&p); @@ -924,7 +1411,7 @@ void AutocompleteEdit::OnContextMenu(HWND window, const CPoint& point) { } } -void AutocompleteEdit::OnCopy() { +void AutocompleteEditView::OnCopy() { const std::wstring text(GetSelectedText()); if (text.empty()) return; @@ -939,20 +1426,16 @@ void AutocompleteEdit::OnCopy() { if (static_cast(text.length()) < GetTextLength()) return; - // The entire control is selected. Let's see what the user typed. Usually - // we'd use GetDesiredTLD() to figure out the TLD, but right now the user is - // probably holding down control to cause the copy (which would make us always - // think the user wanted ".com" added). - url_parse::Parsed parts; - const AutocompleteInput::Type type = AutocompleteInput::Parse( - UserTextFromDisplayText(text), std::wstring(), &parts, NULL); - if (type == AutocompleteInput::URL) { - const GURL url(URLFixerUpper::FixupURL(text, std::wstring())); + // The entire control is selected. Let's see what the user typed. We can't + // use model_->CurrentTextIsURL() or model_->GetDataForURLExport() because + // right now the user is probably holding down control to cause the copy, + // which will screw up our calculation of the desired_tld. + GURL url; + if (model_->GetURLForText(text, &url)) clipboard->WriteHyperlink(text, url.spec()); - } } -void AutocompleteEdit::OnCut() { +void AutocompleteEditView::OnCut() { OnCopy(); // This replace selection will have no effect (even on the undo stack) if the @@ -960,7 +1443,9 @@ void AutocompleteEdit::OnCut() { ReplaceSel(L"", true); } -LRESULT AutocompleteEdit::OnGetObject(UINT uMsg, WPARAM wparam, LPARAM lparam) { +LRESULT AutocompleteEditView::OnGetObject(UINT uMsg, + WPARAM wparam, + LPARAM lparam) { // Accessibility readers will send an OBJID_CLIENT message. if (lparam == OBJID_CLIENT) { // Re-attach for internal re-usage of accessibility pointer. @@ -974,7 +1459,7 @@ LRESULT AutocompleteEdit::OnGetObject(UINT uMsg, WPARAM wparam, LPARAM lparam) { return 0; } -LRESULT AutocompleteEdit::OnImeComposition(UINT message, +LRESULT AutocompleteEditView::OnImeComposition(UINT message, WPARAM wparam, LPARAM lparam) { ScopedFreeze freeze(this, GetTextObjectModel()); @@ -993,7 +1478,7 @@ LRESULT AutocompleteEdit::OnImeComposition(UINT message, return result; } -void AutocompleteEdit::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) { +void AutocompleteEditView::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) { if (OnKeyDownAllModes(key, repeat_count, flags) || popup_window_mode_ || OnKeyDownOnlyWritable(key, repeat_count, flags)) return; @@ -1003,42 +1488,28 @@ void AutocompleteEdit::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) { HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags); } -void AutocompleteEdit::OnKeyUp(TCHAR key, UINT repeat_count, UINT flags) { - if ((key == VK_CONTROL) && (control_key_state_ != UP)) { - control_key_state_ = UP; - if (popup_->is_open()) { - // Autocomplete history provider results may change, so refresh the - // popup. This will force user_input_in_progress_ to true, but if the - // popup is open, that should have already been the case. - UpdatePopup(); - } - } +void AutocompleteEditView::OnKeyUp(TCHAR key, UINT repeat_count, UINT flags) { + if (key == VK_CONTROL) + model_->OnControlKeyChanged(false); SetMsgHandled(false); } -void AutocompleteEdit::OnKillFocus(HWND focus_wnd) { +void AutocompleteEditView::OnKillFocus(HWND focus_wnd) { if (m_hWnd == focus_wnd) { // Focus isn't actually leaving. SetMsgHandled(false); return; } - has_focus_ = false; - control_key_state_ = UP; - paste_state_ = NONE; - // Close the popup. ClosePopup(); // Save the user's existing selection to restore it later. GetSelection(saved_selection_for_focus_change_); - // Like typing, killing focus "accepts" the temporary text as the user - // text, because it makes little sense to have temporary text when the - // popup is closed. - InternalSetUserText(UserTextFromDisplayText(GetText())); - has_temporary_text_ = false; + // Tell the model to reset itself. + model_->OnKillFocus(); // Let the CRichEditCtrl do its default handling. This will complete any // in-progress IME composition. We must do this after setting has_focus_ to @@ -1051,7 +1522,8 @@ void AutocompleteEdit::OnKillFocus(HWND focus_wnd) { // the controller that input is in progress, which could cause the visible // hints to change. (I don't know if there's a real scenario where they // actually do change, but this is safest.) - if (show_search_hint_ || (is_keyword_hint_ && !keyword_.empty())) + if (model_->show_search_hint() || + (model_->is_keyword_hint() && !model_->keyword().empty())) controller_->OnChanged(); // Cancel any user selection and scroll the text back to the beginning of the @@ -1061,7 +1533,7 @@ void AutocompleteEdit::OnKillFocus(HWND focus_wnd) { PlaceCaretAt(0); } -void AutocompleteEdit::OnLButtonDblClk(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnLButtonDblClk(UINT keys, const CPoint& point) { // Save the double click info for later triple-click detection. tracking_double_click_ = true; double_click_point_ = point; @@ -1079,7 +1551,7 @@ void AutocompleteEdit::OnLButtonDblClk(UINT keys, const CPoint& point) { gaining_focus_.reset(); // See NOTE in OnMouseActivate(). } -void AutocompleteEdit::OnLButtonDown(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnLButtonDown(UINT keys, const CPoint& point) { if (gaining_focus_.get()) { // This click is giving us focus, so we need to track how much the mouse // moves to see if it's a drag or just a click. Clicks should select all @@ -1129,7 +1601,7 @@ void AutocompleteEdit::OnLButtonDown(UINT keys, const CPoint& point) { gaining_focus_.reset(); } -void AutocompleteEdit::OnLButtonUp(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnLButtonUp(UINT keys, const CPoint& point) { // default processing should happen first so we can see the result of the // selection ScopedFreeze freeze(this, GetTextObjectModel()); @@ -1149,9 +1621,9 @@ void AutocompleteEdit::OnLButtonUp(UINT keys, const CPoint& point) { UpdateDragDone(keys); } -LRESULT AutocompleteEdit::OnMouseActivate(HWND window, - UINT hit_test, - UINT mouse_message) { +LRESULT AutocompleteEditView::OnMouseActivate(HWND window, + UINT hit_test, + UINT mouse_message) { // First, give other handlers a chance to handle the message to see if we are // actually going to activate and gain focus. LRESULT result = DefWindowProc(WM_MOUSEACTIVATE, @@ -1162,7 +1634,7 @@ LRESULT AutocompleteEdit::OnMouseActivate(HWND window, // reached before OnLButtonDown(), preventing us from detecting this properly // there. Also in those cases, we need to already know in OnSetFocus() that // we should not restore the saved selection. - if (!has_focus_ && (mouse_message == WM_LBUTTONDOWN) && + if (!model_->has_focus() && (mouse_message == WM_LBUTTONDOWN) && (result == MA_ACTIVATE)) { DCHECK(!gaining_focus_.get()); gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel())); @@ -1179,7 +1651,7 @@ LRESULT AutocompleteEdit::OnMouseActivate(HWND window, return result; } -void AutocompleteEdit::OnMouseMove(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnMouseMove(UINT keys, const CPoint& point) { if (possible_drag_) { StartDragIfNecessary(point); // Don't fall through to default mouse handling, otherwise a second @@ -1252,7 +1724,7 @@ void AutocompleteEdit::OnMouseMove(UINT keys, const CPoint& point) { } } -void AutocompleteEdit::OnPaint(HDC bogus_hdc) { +void AutocompleteEditView::OnPaint(HDC bogus_hdc) { // We need to paint over the top of the edit. If we simply let the edit do // its default painting, then do ours into the window DC, the screen is // updated in between and we can get flicker. To avoid this, we force the @@ -1316,7 +1788,7 @@ void AutocompleteEdit::OnPaint(HDC bogus_hdc) { edit_hwnd = old_edit_hwnd; } -void AutocompleteEdit::OnNonLButtonDown(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnNonLButtonDown(UINT keys, const CPoint& point) { // Interestingly, the edit doesn't seem to cancel triple clicking when the // x-buttons (which usually means "thumb buttons") are pressed, so we only // call this for M and R down. @@ -1327,14 +1799,14 @@ void AutocompleteEdit::OnNonLButtonDown(UINT keys, const CPoint& point) { SetMsgHandled(false); } -void AutocompleteEdit::OnNonLButtonUp(UINT keys, const CPoint& point) { +void AutocompleteEditView::OnNonLButtonUp(UINT keys, const CPoint& point) { UpdateDragDone(keys); // Let default handler have a crack at this. SetMsgHandled(false); } -void AutocompleteEdit::OnPaste() { +void AutocompleteEditView::OnPaste() { // Replace the selection if we have something to paste. const std::wstring text(GetClipboardText()); if (!text.empty()) { @@ -1343,18 +1815,18 @@ void AutocompleteEdit::OnPaste() { CHARRANGE sel; GetSel(sel); if (IsSelectAll(sel)) - paste_state_ = REPLACING_ALL; + model_->on_paste_replacing_all(); ReplaceSel(text.c_str(), true); } } -void AutocompleteEdit::OnSetFocus(HWND focus_wnd) { - has_focus_ = true; - control_key_state_ = (GetKeyState(VK_CONTROL) < 0) ? DOWN_WITHOUT_CHANGE : UP; +void AutocompleteEditView::OnSetFocus(HWND focus_wnd) { + model_->OnSetFocus(GetKeyState(VK_CONTROL) < 0); // Notify controller if it needs to show hint UI of some kind. ScopedFreeze freeze(this, GetTextObjectModel()); - if (show_search_hint_ || (is_keyword_hint_ && !keyword_.empty())) + if (model_->show_search_hint() || + (model_->is_keyword_hint() && !model_->keyword().empty())) controller_->OnChanged(); // Restore saved selection if available. @@ -1366,7 +1838,7 @@ void AutocompleteEdit::OnSetFocus(HWND focus_wnd) { SetMsgHandled(false); } -void AutocompleteEdit::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { +void AutocompleteEditView::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { // Nearly all alt- combos result in beeping rather than doing something // useful, so we discard most. Exceptions: // * ctrl-alt-, which is sometimes important, generates WM_CHAR instead @@ -1378,17 +1850,19 @@ void AutocompleteEdit::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { SetMsgHandled(false); } -void AutocompleteEdit::HandleKeystroke(UINT message, TCHAR key, - UINT repeat_count, UINT flags) { +void AutocompleteEditView::HandleKeystroke(UINT message, + TCHAR key, + UINT repeat_count, + UINT flags) { ScopedFreeze freeze(this, GetTextObjectModel()); OnBeforePossibleChange(); DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); OnAfterPossibleChange(); } -bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, - UINT repeat_count, - UINT flags) { +bool AutocompleteEditView::OnKeyDownOnlyWritable(TCHAR key, + UINT repeat_count, + UINT flags) { // NOTE: Annoyingly, ctrl-alt- generates WM_KEYDOWN rather than // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places // in this function even with a WM_SYSKEYDOWN handler. @@ -1396,8 +1870,8 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, int count = repeat_count; switch (key) { case VK_RETURN: - AcceptInput((flags & KF_ALTDOWN) ? NEW_FOREGROUND_TAB : CURRENT_TAB, - false); + model_->AcceptInput((flags & KF_ALTDOWN) ? + NEW_FOREGROUND_TAB : CURRENT_TAB, false); return true; case VK_UP: @@ -1407,38 +1881,7 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, if (flags & KF_ALTDOWN) return false; - // NOTE: VK_DOWN/VK_UP purposefully don't trigger any code that resets - // paste_state_. - disable_keyword_ui_ = false; - if (!popup_->is_open()) { - if (!popup_->query_in_progress()) { - // The popup is neither open nor working on a query already. So, - // start an autocomplete query for the current text. This also sets - // user_input_in_progress_ to true, which we want: if the user has - // started to interact with the popup, changing the permanent_text_ - // shouldn't change the displayed text. - // Note: This does not force the popup to open immediately. - if (!user_input_in_progress_) - InternalSetUserText(permanent_text_); - DCHECK(user_text_ == UserTextFromDisplayText(GetText())); - UpdatePopup(); - } - - // Now go ahead and force the popup to open, and copy the text of the - // default item into the edit. We ignore |count|, since without the - // popup open, the user doesn't really know what they're interacting - // with. Since the user hit an arrow key to explicitly open the popup, - // we assume that they prefer the temporary text of the default item - // to their own text, like we do when they arrow around an already-open - // popup. In many cases the existing text in the edit and the new text - // will be the same, and the only visible effect will be to cancel any - // selection and place the cursor at the end of the edit. - popup_->Move(0); - } else { - // The popup is open, so the user should be able to interact with it - // normally. - popup_->Move(count); - } + model_->OnUpOrDownKeyPressed(count); return true; // Hijacking Editing Commands @@ -1463,7 +1906,7 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, case VK_DELETE: if ((flags & KF_ALTDOWN) || GetKeyState(VK_SHIFT) >= 0) return false; - if (control_key_state_ == UP) { + if (GetKeyState(VK_CONTROL) >= 0) { // Cut text if possible. CHARRANGE selection; GetSel(selection); @@ -1472,18 +1915,18 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, OnBeforePossibleChange(); Cut(); OnAfterPossibleChange(); - } else if (popup_->is_open()) { + } else if (popup_model_->is_open()) { // This is a bit overloaded, but we hijack Shift-Delete in this // case to delete the current item from the pop-up. We prefer cutting // to this when possible since that's the behavior more people expect // from Shift-Delete, and it's more commonly useful. - popup_->TryDeletingCurrentItem(); + popup_model_->TryDeletingCurrentItem(); } } return true; case 'X': - if ((flags & KF_ALTDOWN) || (control_key_state_ == UP)) + if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0)) return false; if (GetKeyState(VK_SHIFT) >= 0) { ScopedFreeze freeze(this, GetTextObjectModel()); @@ -1495,11 +1938,10 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, case VK_INSERT: case 'V': - if ((flags & KF_ALTDOWN) || ((key == 'V') ? - (control_key_state_ == UP) : (GetKeyState(VK_SHIFT) >= 0))) + if ((flags & KF_ALTDOWN) || + (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0)) return false; - if ((key == 'V') ? - (GetKeyState(VK_SHIFT) >= 0) : (control_key_state_ == UP)) { + if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) { ScopedFreeze freeze(this, GetTextObjectModel()); OnBeforePossibleChange(); Paste(); @@ -1508,7 +1950,8 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, return true; case VK_BACK: { - if ((flags & KF_ALTDOWN) || is_keyword_hint_ || keyword_.empty()) + if ((flags & KF_ALTDOWN) || model_->is_keyword_hint() || + model_->keyword().empty()) return false; { @@ -1519,41 +1962,17 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, } // We're showing a keyword and the user pressed backspace at the beginning - // of the text. Delete the trailing space from the keyword forcing the - // selected keyword to become empty. + // of the text. Delete the selected keyword. ScopedFreeze freeze(this, GetTextObjectModel()); - OnBeforePossibleChange(); - const std::wstring window_text(keyword_ + GetText()); - SetWindowText(window_text.c_str()); - PlaceCaretAt(keyword_.length()); - popup_->manually_selected_match_.Clear(); - keyword_.clear(); - OnAfterPossibleChange(); - just_deleted_text_ = true; // OnAfterPossibleChange() fails to clear this - // since the edit contents have actually grown - // longer. + model_->ClearKeyword(GetText()); return true; } case VK_TAB: { - if (is_keyword_hint_ && !keyword_.empty()) { + if (model_->is_keyword_hint() && !model_->keyword().empty()) { // Accept the keyword. ScopedFreeze freeze(this, GetTextObjectModel()); - OnBeforePossibleChange(); - SetWindowText(L""); - popup_->manually_selected_match_.Clear(); - popup_->manually_selected_match_.provider_affinity = - popup_->autocomplete_controller()->keyword_provider(); - is_keyword_hint_ = false; - disable_keyword_ui_ = false; - OnAfterPossibleChange(); - just_deleted_text_ = false; // OnAfterPossibleChange() erroneously sets - // this since the edit contents have - // disappeared. It doesn't really matter, - // but we clear it to be consistent. - - // Send out notification (primarily for logging). - UserMetrics::RecordAction(L"AcceptedKeywordHint", profile_); + model_->AcceptKeyword(); } return true; } @@ -1566,28 +1985,19 @@ bool AutocompleteEdit::OnKeyDownOnlyWritable(TCHAR key, } } -bool AutocompleteEdit::OnKeyDownAllModes(TCHAR key, - UINT repeat_count, - UINT flags) { +bool AutocompleteEditView::OnKeyDownAllModes(TCHAR key, + UINT repeat_count, + UINT flags) { // See KF_ALTDOWN comment atop OnKeyDownOnlyWriteable(). switch (key) { case VK_CONTROL: - if (control_key_state_ == UP) { - control_key_state_ = DOWN_WITHOUT_CHANGE; - if (popup_->is_open()) { - DCHECK(!popup_window_mode_); // How did the popup get open in read-only mode? - // Autocomplete history provider results may change, so refresh the - // popup. This will force user_input_in_progress_ to true, but if the - // popup is open, that should have already been the case. - UpdatePopup(); - } - } + model_->OnControlKeyChanged(true); return false; case 'C': // See more detailed comments in OnKeyDownOnlyWriteable(). - if ((flags & KF_ALTDOWN) || (control_key_state_ == UP)) + if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0)) return false; if (GetKeyState(VK_SHIFT) >= 0) Copy(); @@ -1598,134 +2008,7 @@ bool AutocompleteEdit::OnKeyDownAllModes(TCHAR key, } } -void AutocompleteEdit::OnBeforePossibleChange() { - // Record our state. - text_before_change_ = GetText(); - GetSelection(sel_before_change_); - select_all_before_change_ = IsSelectAll(sel_before_change_); -} - -bool AutocompleteEdit::OnAfterPossibleChange() { - // Prevent the user from selecting the "phantom newline" at the end of the - // edit. If they try, we just silently move the end of the selection back to - // the end of the real text. - CHARRANGE new_sel; - GetSelection(new_sel); - const int length = GetTextLength(); - if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) { - if (new_sel.cpMin > length) - new_sel.cpMin = length; - if (new_sel.cpMax > length) - new_sel.cpMax = length; - SetSelectionRange(new_sel); - } - const bool selection_differs = (new_sel.cpMin != sel_before_change_.cpMin) || - (new_sel.cpMax != sel_before_change_.cpMax); - - // See if the text or selection have changed since OnBeforePossibleChange(). - const std::wstring new_text(GetText()); - const bool text_differs = (new_text != text_before_change_); - - // Update the paste state as appropriate: if we're just finishing a paste - // that replaced all the text, preserve that information; otherwise, if we've - // made some other edit, clear paste tracking. - if (paste_state_ == REPLACING_ALL) - paste_state_ = REPLACED_ALL; - else if (text_differs) - paste_state_ = NONE; - - // If something has changed while the control key is down, prevent - // "ctrl-enter" until the control key is released. When we do this, we need - // to update the popup if it's open, since the desired_tld will have changed. - if ((text_differs || selection_differs) && - (control_key_state_ == DOWN_WITHOUT_CHANGE)) { - control_key_state_ = DOWN_WITH_CHANGE; - if (!text_differs && !popup_->is_open()) - return false; // Don't open the popup for no reason. - } else if (!text_differs && - (inline_autocomplete_text_.empty() || !selection_differs)) { - return false; - } - - const bool had_keyword = !is_keyword_hint_ && !keyword_.empty(); - - // Modifying the selection counts as accepting the autocompleted text. - InternalSetUserText(UserTextFromDisplayText(new_text)); - has_temporary_text_ = false; - - if (text_differs) { - // When the user has deleted text, don't allow inline autocomplete. Make - // sure to not flag cases like selecting part of the text and then pasting - // (or typing) the prefix of that selection. (We detect these by making - // sure the caret, which should be after any insertion, hasn't moved - // forward of the old selection start.) - just_deleted_text_ = (text_before_change_.length() > new_text.length()) && - (new_sel.cpMin <= std::min(sel_before_change_.cpMin, - sel_before_change_.cpMax)); - - // When the user doesn't have a selected keyword, deleting text or replacing - // all of it with something else should reset the provider affinity. The - // typical use case for deleting is that the user starts typing, sees that - // some entry is close to what he wants, arrows to it, and then deletes some - // unnecessary bit from the end of the string. In this case the user didn't - // actually want "provider X", he wanted the string from that entry for - // editing purposes, and he's no longer looking at the popup to notice that, - // despite deleting some text, the action we'll take on enter hasn't changed - // at all. - if (!had_keyword && (just_deleted_text_ || select_all_before_change_)) { - popup_->manually_selected_match_.Clear(); - } - } - - // Disable the fancy keyword UI if the user didn't already have a visible - // keyword and is not at the end of the edit. This prevents us from showing - // the fancy UI (and interrupting the user's editing) if the user happens to - // have a keyword for 'a', types 'ab' then puts a space between the 'a' and - // the 'b'. - disable_keyword_ui_ = (is_keyword_hint_ || keyword_.empty()) && - ((new_sel.cpMax != length) || (new_sel.cpMin != length)); - - UpdatePopup(); - - if (!had_keyword && !is_keyword_hint_ && !keyword_.empty()) { - // Went from no selected keyword to a selected keyword. Set the affinity to - // the keyword provider. This forces the selected keyword to persist even - // if the user deletes all the text. - popup_->manually_selected_match_.Clear(); - popup_->manually_selected_match_.provider_affinity = - popup_->autocomplete_controller()->keyword_provider(); - } - - if (text_differs) - TextChanged(); - - return true; -} - -void AutocompleteEdit::SetUserText(const std::wstring& text, - const std::wstring& display_text, - bool update_popup) { - ScopedFreeze freeze(this, GetTextObjectModel()); - SetInputInProgress(true); - paste_state_ = NONE; - InternalSetUserText(text); - SetWindowText(display_text.c_str()); - PlaceCaretAt(display_text.length()); - saved_selection_for_focus_change_.cpMin = -1; - has_temporary_text_ = false; - popup_->manually_selected_match_.Clear(); - if (update_popup) - UpdatePopup(); - TextChanged(); -} - -void AutocompleteEdit::InternalSetUserText(const std::wstring& text) { - user_text_ = text; - just_deleted_text_ = false; - inline_autocomplete_text_.clear(); -} - -void AutocompleteEdit::GetSelection(CHARRANGE& sel) const { +void AutocompleteEditView::GetSelection(CHARRANGE& sel) const { GetSel(sel); // See if we need to reverse the direction of the selection. @@ -1738,7 +2021,7 @@ void AutocompleteEdit::GetSelection(CHARRANGE& sel) const { std::swap(sel.cpMin, sel.cpMax); } -std::wstring AutocompleteEdit::GetSelectedText() const { +std::wstring AutocompleteEditView::GetSelectedText() const { // Figure out the length of the selection. CHARRANGE sel; GetSel(sel); @@ -1749,7 +2032,7 @@ std::wstring AutocompleteEdit::GetSelectedText() const { return str; } -void AutocompleteEdit::SetSelection(LONG start, LONG end) { +void AutocompleteEditView::SetSelection(LONG start, LONG end) { SetSel(start, end); if (start <= end) @@ -1762,18 +2045,18 @@ void AutocompleteEdit::SetSelection(LONG start, LONG end) { selection->SetFlags(tomSelStartActive); } -void AutocompleteEdit::PlaceCaretAt(std::wstring::size_type pos) { +void AutocompleteEditView::PlaceCaretAt(std::wstring::size_type pos) { SetSelection(static_cast(pos), static_cast(pos)); } -bool AutocompleteEdit::IsSelectAll(const CHARRANGE& sel) const { +bool AutocompleteEditView::IsSelectAll(const CHARRANGE& sel) const { const int text_length = GetTextLength(); return ((sel.cpMin == 0) && (sel.cpMax >= text_length)) || ((sel.cpMax == 0) && (sel.cpMin >= text_length)); } -LONG AutocompleteEdit::ClipXCoordToVisibleText(LONG x, - bool is_triple_click) const { +LONG AutocompleteEditView::ClipXCoordToVisibleText(LONG x, + bool is_triple_click) const { // Clip the X coordinate to the left edge of the text. Careful: // PosFromChar(0) may return a negative X coordinate if the beginning of the // text has scrolled off the edit, so don't go past the clip rect's edge. @@ -1804,7 +2087,7 @@ LONG AutocompleteEdit::ClipXCoordToVisibleText(LONG x, return is_triple_click ? (right_bound - 1) : right_bound; } -void AutocompleteEdit::EmphasizeURLComponents() { +void AutocompleteEditView::EmphasizeURLComponents() { ITextDocument* const text_object_model = GetTextObjectModel(); ScopedFreeze freeze(this, text_object_model); ScopedSuspendUndo suspend_undo(text_object_model); @@ -1814,31 +2097,20 @@ void AutocompleteEdit::EmphasizeURLComponents() { GetSelection(saved_sel); // See whether the contents are a URL with a non-empty host portion, which we - // should emphasize. + // should emphasize. To check for a URL, rather than using the type returned + // by Parse(), ask the model, which will check the desired page transition for + // this input. This can tell us whether an UNKNOWN input string is going to + // be treated as a search or a navigation, and is the same method the Paste + // And Go system uses. url_parse::Parsed parts; - AutocompleteInput::Parse(GetText(), GetDesiredTLD(), &parts, NULL); - bool emphasize = (parts.host.len > 0); - // If !user_input_in_progress_, the permanent text is showing, which should - // always be a URL, so no further checking is needed. By avoiding checking in - // this case, we avoid calling into the autocomplete providers, and thus - // initializing the history system, as long as possible, which speeds startup. - if (user_input_in_progress_) { - // Rather than using the type returned by Parse(), key off the desired page - // transition for this input since that can tell us whether an UNKNOWN input - // string is going to be treated as a search or a navigation. This is the - // same method the Paste And Go system uses. - PageTransition::Type transition = PageTransition::LINK; - GetURLForCurrentText(&transition, NULL, NULL); - if (transition != PageTransition::TYPED) - emphasize = false; - } + AutocompleteInput::Parse(GetText(), model_->GetDesiredTLD(), &parts, NULL); + const bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); // Set the baseline emphasis. CHARFORMAT cf = {0}; cf.dwMask = CFM_COLOR; cf.dwEffects = 0; - cf.crTextColor = (emphasize ? GetSysColor(COLOR_GRAYTEXT) : - GetSysColor(COLOR_WINDOWTEXT)); + cf.crTextColor = GetSysColor(emphasize ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT); SelectAll(false); SetSelectionCharFormat(cf); @@ -1851,9 +2123,8 @@ void AutocompleteEdit::EmphasizeURLComponents() { // Emphasize the scheme for security UI display purposes (if necessary). insecure_scheme_component_.reset(); - if (!user_input_in_progress_ && parts.scheme.is_nonempty() && - ((scheme_security_level_ == ToolbarModel::SECURE) || - (scheme_security_level_ == ToolbarModel::INSECURE))) { + if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && + (scheme_security_level_ != ToolbarModel::NORMAL)) { if (scheme_security_level_ == ToolbarModel::SECURE) { cf.crTextColor = kSecureSchemeColor; } else { @@ -1869,9 +2140,9 @@ void AutocompleteEdit::EmphasizeURLComponents() { SetSelectionRange(saved_sel); } -void AutocompleteEdit::EraseTopOfSelection(CDC* dc, - const CRect& client_rect, - const CRect& paint_clip_rect) { +void AutocompleteEditView::EraseTopOfSelection(CDC* dc, + const CRect& client_rect, + const CRect& paint_clip_rect) { // Find the area we care about painting. We could calculate the rect // containing just the selected portion, but there's no harm in simply erasing // the whole top of the client area, and at least once I saw us manage to @@ -1886,7 +2157,7 @@ void AutocompleteEdit::EraseTopOfSelection(CDC* dc, dc->FillSolidRect(&erase_rect, background_color_); } -void AutocompleteEdit::DrawSlashForInsecureScheme( +void AutocompleteEditView::DrawSlashForInsecureScheme( HDC hdc, const CRect& client_rect, const CRect& paint_clip_rect) { @@ -1978,9 +2249,9 @@ void AutocompleteEdit::DrawSlashForInsecureScheme( canvas_clip_rect.top, &canvas_paint_clip_rect); } -void AutocompleteEdit::DrawDropHighlight(HDC hdc, - const CRect& client_rect, - const CRect& paint_clip_rect) { +void AutocompleteEditView::DrawDropHighlight(HDC hdc, + const CRect& client_rect, + const CRect& paint_clip_rect) { DCHECK(drop_highlight_position_ != -1); const int highlight_y = client_rect.top + font_y_adjustment_; @@ -2002,65 +2273,13 @@ void AutocompleteEdit::DrawDropHighlight(HDC hdc, DeleteObject(SelectObject(hdc, last_pen)); } -std::wstring AutocompleteEdit::GetDesiredTLD() const { - return (control_key_state_ == DOWN_WITHOUT_CHANGE) ? L"com" : L""; -} - -void AutocompleteEdit::UpdatePopup() { - ScopedFreeze freeze(this, GetTextObjectModel()); - SetInputInProgress(true); - - if (!has_focus_) { - // When we're in the midst of losing focus, don't rerun autocomplete. This - // can happen when losing focus causes the IME to cancel/finalize a - // composition. We still want to note that user input is in progress, we - // just don't want to do anything else. - // - // Note that in this case the ScopedFreeze above was unnecessary; however, - // we're inside the callstack of OnKillFocus(), which has already frozen the - // edit, so this will never result in an unnecessary UpdateWindow() call. - return; - } - - // Figure out whether the user is trying to compose something in an IME. - bool ime_composing = false; - HIMC context = ImmGetContext(m_hWnd); - if (context) { - ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0); - ImmReleaseContext(m_hWnd, context); - } - - // 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 - CHARRANGE sel; - GetSel(sel); - popup_->StartAutocomplete(user_text_, GetDesiredTLD(), - just_deleted_text_ || (sel.cpMax < GetTextLength()) || - (paste_state_ != NONE) || ime_composing); -} - -void AutocompleteEdit::TextChanged() { +void AutocompleteEditView::TextChanged() { ScopedFreeze freeze(this, GetTextObjectModel()); EmphasizeURLComponents(); controller_->OnChanged(); } -std::wstring AutocompleteEdit::DisplayTextFromUserText( - const std::wstring& text) const { - return (is_keyword_hint_ || keyword_.empty()) ? - text : KeywordProvider::SplitReplacementStringFromInput(text); -} - -std::wstring AutocompleteEdit::UserTextFromDisplayText( - const std::wstring& text) const { - return (is_keyword_hint_ || keyword_.empty()) ? - text : (keyword_ + L" " + text); -} - -std::wstring AutocompleteEdit::GetClipboardText() const { +std::wstring AutocompleteEditView::GetClipboardText() const { // Try text format. ClipboardService* clipboard = g_browser_process->clipboard_service(); if (clipboard->IsFormatAvailable(CF_UNICODETEXT)) { @@ -2095,93 +2314,11 @@ std::wstring AutocompleteEdit::GetClipboardText() const { return std::wstring(); } -bool AutocompleteEdit::CanPasteAndGo(const std::wstring& text) const { - if (popup_window_mode_) - return false; - - // Reset local state. - paste_and_go_url_.clear(); - paste_and_go_transition_ = PageTransition::TYPED; - paste_and_go_alternate_nav_url_.clear(); - - // See if the clipboard text can be parsed. - const AutocompleteInput input(text, std::wstring(), true); - if (input.type() == AutocompleteInput::INVALID) - return false; - - // Ask the controller what do do with this input. - paste_and_go_controller->SetProfile(profile_); - // This is cheap, and since there's one - // paste_and_go_controller for many tabs which - // may all have different profiles, it ensures - // we're always using the right one. - const bool done = paste_and_go_controller->Start(input, false, true); - DCHECK(done); - AutocompleteResult result; - paste_and_go_controller->GetResult(&result); - if (result.empty()) - return false; - - // Set local state based on the default action for this input. - result.SetDefaultMatch(AutocompleteResult::Selection()); - const AutocompleteResult::const_iterator match(result.default_match()); - DCHECK(match != result.end()); - paste_and_go_url_ = match->destination_url; - paste_and_go_transition_ = match->transition; - paste_and_go_alternate_nav_url_ = result.GetAlternateNavURL(input, match); - - return !paste_and_go_url_.empty(); -} - -void AutocompleteEdit::PasteAndGo() { - // The final parameter to OpenURL, keyword, is not quite correct here: it's - // possible to "paste and go" a string that contains a keyword. This is - // enough of an edge case that we ignore this possibility. - RevertAll(); - OpenURL(paste_and_go_url_, CURRENT_TAB, paste_and_go_transition_, - paste_and_go_alternate_nav_url_, AutocompletePopupModel::kNoMatch, - std::wstring()); -} - -void AutocompleteEdit::SetInputInProgress(bool in_progress) { - if (user_input_in_progress_ == in_progress) - return; - - user_input_in_progress_ = in_progress; - controller_->OnInputInProgress(in_progress); -} - -void AutocompleteEdit::SendOpenNotification(size_t selected_line, - const std::wstring& keyword) { - // We only care about cases where there is a selection (i.e. the popup is - // open). - if (popup_->is_open()) { - scoped_ptr log(popup_->GetAutocompleteLog()); - if (selected_line != AutocompletePopupModel::kNoMatch) - log->selected_index = selected_line; - else if (!has_temporary_text_) - log->inline_autocompleted_length = inline_autocomplete_text_.length(); - NotificationService::current()->Notify( - NOTIFY_OMNIBOX_OPENED_URL, Source(profile_), - Details(log.get())); - } - - TemplateURLModel* template_url_model = profile_->GetTemplateURLModel(); - if (keyword.empty() || !template_url_model) - return; - - const TemplateURL* const template_url = - template_url_model->GetTemplateURLForKeyword(keyword); - if (template_url) { - UserMetrics::RecordAction(L"AcceptedKeyword", profile_); - template_url_model->IncrementUsageCount(template_url); - } - - // NOTE: We purposefully don't increment the usage count of the default search - // engine, if applicable; see comments in template_url.h. +bool AutocompleteEditView::CanPasteAndGo(const std::wstring& text) const { + return !popup_window_mode_ && model_->CanPasteAndGo(text); } -ITextDocument* AutocompleteEdit::GetTextObjectModel() const { +ITextDocument* AutocompleteEditView::GetTextObjectModel() const { if (!text_object_model_) { // This is lazily initialized, instead of being initialized in the // constructor, in order to avoid hurting startup performance. @@ -2192,7 +2329,7 @@ ITextDocument* AutocompleteEdit::GetTextObjectModel() const { return text_object_model_; } -void AutocompleteEdit::StartDragIfNecessary(const CPoint& point) { +void AutocompleteEditView::StartDragIfNecessary(const CPoint& point) { if (initiated_drag_ || !win_util::IsDrag(mouse_down_point_, point)) return; @@ -2220,21 +2357,17 @@ void AutocompleteEdit::StartDragIfNecessary(const CPoint& point) { const std::wstring start_text(GetText()); if (IsSelectAll(sel)) { // All the text is selected, export as URL. - const std::wstring url(GetURLForCurrentText(NULL, NULL, NULL)); + GURL url; std::wstring title; SkBitmap favicon; - if (url == permanent_text_) { - title = controller_->GetTitle(); - favicon = controller_->GetFavIcon(); - } - const GURL gurl(url); - drag_utils::SetURLAndDragImage(gurl, title, favicon, data.get()); - data->SetURL(gurl, title); + model_->GetDataForURLExport(&url, &title, &favicon); + drag_utils::SetURLAndDragImage(url, title, favicon, data.get()); + data->SetURL(url, title); supported_modes |= DROPEFFECT_LINK; - UserMetrics::RecordAction(L"Omnibox_DragURL", profile_); + UserMetrics::RecordAction(L"Omnibox_DragURL", model_->profile()); } else { supported_modes |= DROPEFFECT_MOVE; - UserMetrics::RecordAction(L"Omnibox_DragString", profile_); + UserMetrics::RecordAction(L"Omnibox_DragString", model_->profile()); } data->SetString(GetSelectedText()); @@ -2284,7 +2417,7 @@ void AutocompleteEdit::StartDragIfNecessary(const CPoint& point) { tracking_click_ = false; } -void AutocompleteEdit::OnPossibleDrag(const CPoint& point) { +void AutocompleteEditView::OnPossibleDrag(const CPoint& point) { if (possible_drag_) return; @@ -2304,12 +2437,12 @@ void AutocompleteEdit::OnPossibleDrag(const CPoint& point) { } } -void AutocompleteEdit::UpdateDragDone(UINT keys) { +void AutocompleteEditView::UpdateDragDone(UINT keys) { possible_drag_ = (possible_drag_ && ((keys & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) != 0)); } -void AutocompleteEdit::RepaintDropHighlight(int position) { +void AutocompleteEditView::RepaintDropHighlight(int position) { if ((position != -1) && (position <= GetTextLength())) { const POINT min_loc(PosFromChar(position)); const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_, @@ -2317,4 +2450,3 @@ void AutocompleteEdit::RepaintDropHighlight(int position) { InvalidateRect(&highlight_bounds, false); } } - diff --git a/chrome/browser/autocomplete/autocomplete_edit.h b/chrome/browser/autocomplete/autocomplete_edit.h index 7d1340997e9fb2..832297db9a782f 100644 --- a/chrome/browser/autocomplete/autocomplete_edit.h +++ b/chrome/browser/autocomplete/autocomplete_edit.h @@ -24,79 +24,61 @@ class AutocompletePopupModel; class CommandController; class Profile; class TabContents; - namespace ChromeViews { class View; } -// Provides the implementation of an edit control with a drop-down -// autocomplete box. The box itself is implemented in autocomplete_popup.cc -// This file implements the edit box and management for the popup. -// -// This implementation is currently appropriate for the URL bar, where the -// autocomplete dropdown is always displayed because there is always a -// default item. For web page autofill and other applications, this is -// probably not appropriate. We may want to add a flag to determine which -// of these modes we're in. -class AutocompleteEdit - : public CWindowImpl >, - public CRichEditCommands, - public Menu::Delegate { - public: - DECLARE_WND_CLASS(L"Chrome_AutocompleteEdit"); +class AutocompleteEditController; +class AutocompleteEditModel; +class AutocompleteEditView; +struct AutocompleteEditState; - // Embedders of this control must implement this class. - class Controller { - public: - // When the user presses enter or selects a line with the mouse, this - // function will get called synchronously with the url to open and - // disposition and transition to use when opening it. - // - // |alternate_nav_url|, if non-empty, contains the alternate navigation URL - // for |url|, which the controller can check for existence. See comments on - // AutocompleteResult::GetAlternateNavURL(). - virtual void OnAutocompleteAccept(const std::wstring& url, - WindowOpenDisposition disposition, - PageTransition::Type transition, - const std::wstring& alternate_nav_url) = 0; - - // Called when anything has changed that might affect the layout or contents - // of the views around the edit, including the text of the edit and the - // status of any keyword- or hint-related state. - virtual void OnChanged() = 0; - - // Called whenever the user starts or stops an input session (typing, - // interacting with the edit, etc.). When user input is not in progress, - // the edit is guaranteed to be showing the permanent text. - virtual void OnInputInProgress(bool in_progress) = 0; - - // Returns the favicon of the current page. - virtual SkBitmap GetFavIcon() const = 0; - - // Returns the title of the current page. - virtual std::wstring GetTitle() const = 0; - }; +// TODO(pkasting): http://b/1343512 The names and contents of the classes in +// this file are temporary. I am in hack-and-slash mode right now. - // The State struct contains enough information about the AutocompleteEdit to - // save/restore a user's typing, caret position, etc. across tab changes. We - // explicitly don't preserve things like whether the popup was open as this - // might be weird. +// Embedders of an AutocompleteEdit widget must implement this class. +class AutocompleteEditController { + public: + // When the user presses enter or selects a line with the mouse, this + // function will get called synchronously with the url to open and + // disposition and transition to use when opening it. + // + // |alternate_nav_url|, if non-empty, contains the alternate navigation URL + // for |url|, which the controller can check for existence. See comments on + // AutocompleteResult::GetAlternateNavURL(). + virtual void OnAutocompleteAccept(const std::wstring& url, + WindowOpenDisposition disposition, + PageTransition::Type transition, + const std::wstring& alternate_nav_url) = 0; + + // Called when anything has changed that might affect the layout or contents + // of the views around the edit, including the text of the edit and the + // status of any keyword- or hint-related state. + virtual void OnChanged() = 0; + + // Called whenever the user starts or stops an input session (typing, + // interacting with the edit, etc.). When user input is not in progress, + // the edit is guaranteed to be showing the permanent text. + virtual void OnInputInProgress(bool in_progress) = 0; + + // Returns the favicon of the current page. + virtual SkBitmap GetFavIcon() const = 0; + + // Returns the title of the current page. + virtual std::wstring GetTitle() const = 0; +}; + +class AutocompleteEditModel { + public: struct State { - State(const CHARRANGE& selection, - const CHARRANGE& saved_selection_for_focus_change, - bool user_input_in_progress, + State(bool user_input_in_progress, const std::wstring& user_text, const AutocompleteResult::Selection& manually_selected_match, const std::wstring& keyword, bool is_keyword_hint, bool disable_keyword_ui, bool show_search_hint) - : selection(selection), - saved_selection_for_focus_change(saved_selection_for_focus_change), - user_input_in_progress(user_input_in_progress), + : user_input_in_progress(user_input_in_progress), user_text(user_text), manually_selected_match(manually_selected_match), keyword(keyword), @@ -105,8 +87,6 @@ class AutocompleteEdit show_search_hint(show_search_hint) { } - const CHARRANGE selection; - const CHARRANGE saved_selection_for_focus_change; bool user_input_in_progress; const std::wstring user_text; AutocompleteResult::Selection manually_selected_match; @@ -116,33 +96,226 @@ class AutocompleteEdit const bool show_search_hint; }; - // The given observer is notified when the user selects an item - AutocompleteEdit(const ChromeFont& font, - Controller* controller, - ToolbarModel* model, - ChromeViews::View* parent_view, - HWND hwnd, - Profile* profile, - CommandController* command_controller, - bool popup_window_mode); - ~AutocompleteEdit(); + AutocompleteEditModel(AutocompleteEditView* view, + AutocompleteEditController* controller, + Profile* profile); + ~AutocompleteEditModel(); - // Called when any LocationBarView state changes. If - // |tab_for_state_restoring| is non-NULL, it points to a TabContents whose - // state we should restore. - void Update(const TabContents* tab_for_state_restoring); + void set_popup_model(AutocompletePopupModel* popup_model) { + popup_ = popup_model; + } // Invoked when the profile has changed. void SetProfile(Profile* profile); - // For use when switching tabs, this saves the current state onto the tab so - // that it can be restored during a later call to Update(). - void SaveStateToTab(TabContents* tab); + Profile* profile() const { return profile_; } - // Returns the current text of the edit control, which could be the - // "temporary" text set by the popup, the "permanent" text set by the - // browser, or just whatever the user has currently typed. - std::wstring GetText() const; + // Returns the current state. This assumes we are switching tabs, and changes + // the internal state appropriately. + const State GetStateForTabSwitch(); + + // Restores local state from the saved |state|. + void RestoreState(const State& state); + + // Called when the user wants to export the entire current text as a URL. + // Sets the url, and if known, the title and favicon. + void GetDataForURLExport(GURL* url, std::wstring* title, SkBitmap* favicon); + + // If the user presses ctrl-enter, it means "add .com to the the end". The + // desired TLD is the TLD the user desires to add to the end of the current + // input, if any, based on their control key state and any other actions + // they've taken. + std::wstring GetDesiredTLD() const; + + // Returns true if the current edit contents will be treated as a + // URL/navigation, as opposed to a search. + bool CurrentTextIsURL(); + + // Returns true if |text| (which is display text in the current context) + // parses as a URL, and in that case sets |url| to the calculated URL. + // Subtle note: This ignores the desired_tld_ (unlike GetDataForURLExport() + // and CurrentTextIsURL()). The view needs this because it calls this + // function during copy handling, when the control key is down to trigger the + // copy. + bool GetURLForText(const std::wstring& text, GURL* url) const; + + bool user_input_in_progress() const { return user_input_in_progress_; } + + // Sets the state of user_input_in_progress_, and notifies the observer if + // that state has changed. + void SetInputInProgress(bool in_progress); + + // Updates permanent_text_ to |new_permanent_text|. Returns true if this + // change should be immediately user-visible, because either the user is not + // editing or the edit does not have focus. + bool UpdatePermanentText(const std::wstring& new_permanent_text); + + // Sets the user_text_ to |text|. Only the View should call this. + void SetUserText(const std::wstring& text); + + // Reverts the edit model back to its unedited state (permanent text showing, + // no user input in progress). + void Revert(); + + // Directs the popup to start autocomplete. + void StartAutocomplete(bool prevent_inline_autocomplete) const; + + // Determines whether the user can "paste and go", given the specified text. + // This also updates the internal paste-and-go-related state variables as + // appropriate so that the controller doesn't need to be repeatedly queried + // for the same text in every clipboard-related function. + bool CanPasteAndGo(const std::wstring& text) const; + + // Navigates to the destination last supplied to CanPasteAndGo. + void PasteAndGo(); + + // Returns true if this is a paste-and-search rather than paste-and-go (or + // nothing). + bool is_paste_and_search() const { + return (paste_and_go_transition_ != PageTransition::TYPED); + } + + // Asks the browser to load the popup's currently selected item, using the + // supplied disposition. This may close the popup. If |for_drop| is true, + // it indicates the input is being accepted as part of a drop operation and + // the transition should be treated as LINK (so that it won't trigger the + // URL to be autocompleted). + void AcceptInput(WindowOpenDisposition disposition, + bool for_drop); + + // As necessary, sends out notification that the user is accepting a URL in + // the edit. If the accepted URL is from selecting a keyword, |keyword| is + // the selected keyword. + // If |selected_line| is kNoMatch, the currently selected line is used for the + // metrics log record; otherwise, the provided value is used as the selected + // line. This is used when the user opens a URL without actually selecting + // its entry, such as middle-clicking it. + void SendOpenNotification(size_t selected_line, const std::wstring& keyword); + + bool has_focus() const { return has_focus_; } + + // Accessors for keyword-related state (see comments on keyword_ and + // is_keyword_hint_). + std::wstring keyword() const { + return ((is_keyword_hint_ && has_focus_) || + (!is_keyword_hint_ && !disable_keyword_ui_)) ? + keyword_ : std::wstring(); + } + bool is_keyword_hint() const { return is_keyword_hint_; } + + // Accepts the current keyword hint as a keyword. + void AcceptKeyword(); + + // Clears the current keyword. |visible_text| is the (non-keyword) text + // currently visible in the edit. + void ClearKeyword(const std::wstring& visible_text); + + // True if we should show the "Type to search" hint (see comments on + // show_search_hint_). + bool show_search_hint() const { return has_focus_ && show_search_hint_; } + + // Returns true if a query to an autocomplete provider is currently + // in progress. This logic should in the future live in + // AutocompleteController but resides here for now. This method is used by + // AutomationProvider::AutocompleteEditIsQueryInProgress. + bool query_in_progress() const; + + // Returns the lastest autocomplete results. This logic should in the future + // live in AutocompleteController but resides here for now. This method is + // used by AutomationProvider::AutocompleteEditGetMatches. + const AutocompleteResult* latest_result() const; + + // Called when the view is gaining focus. |control_down| is whether the + // control key is down (at the time we're gaining focus). + void OnSetFocus(bool control_down); + + // Called when the view is losing focus. Resets some state. + void OnKillFocus(); + + // Called when the user presses the escape key. Decides what, if anything, to + // revert about any current edits. Returns whether the key was handled. + bool OnEscapeKeyPressed(); + + // Called when the user presses or releases the control key. Changes state as + // necessary. + void OnControlKeyChanged(bool pressed); + + // Called when the user pastes in text that replaces the entire edit contents. + void on_paste_replacing_all() { paste_state_ = REPLACING_ALL; } + + // Called when the user presses up or down. |count| is a repeat count, + // negative for moving up, positive for moving down. + void OnUpOrDownKeyPressed(int count); + + // Called back by the AutocompletePopupModel when any relevant data changes. + // This rolls together several separate pieces of data into one call so we can + // update all the UI efficiently: + // |text| is either the new temporary text (if |is_temporary_text| is true) + // from the user manually selecting a different match, or the inline + // autocomplete text (if |is_temporary_text| is false). + // |previous_selected_match| is only used when changing the temporary text; + // it is the match that was (manually or automatically) selected before + // the current manual selection, and is saved to be restored later if the + // user hits . + // |can_show_search_hint| is true if the current choice is nonexistent or a + // search result; in these cases it may be OK to show the "Type to search" + // hint (see comments on show_search_hint_). + // |keyword| is the keyword to show a hint for if |is_keyword_hint| is true, + // or the currently selected keyword if |is_keyword_hint| is false (see + // comments on keyword_ and is_keyword_hint_). + void OnPopupDataChanged( + const std::wstring& text, + bool is_temporary_text, + const AutocompleteResult::Selection& previous_selected_match, + const std::wstring& keyword, + bool is_keyword_hint, + bool can_show_search_hint); + + // Called by the AutocompleteEditView after something changes, with details + // about what state changes occured. Updates internal state, updates the + // popup if necessary, and returns true if any significant changes occurred. + bool OnAfterPossibleChange(const std::wstring& new_text, + bool selection_differs, + bool select_all_before_change, + bool text_differs, + bool just_deleted_text, + bool at_end_of_edit); + + private: + enum ControlKeyState { + UP, // The control key is not depressed. + DOWN_WITHOUT_CHANGE, // The control key is depressed, and the edit's + // contents/selection have not changed since it was + // depressed. This is the only state in which we + // do the "ctrl-enter" behavior when the user hits + // enter. + DOWN_WITH_CHANGE, // The control key is depressed, and the edit's + // contents/selection have changed since it was + // depressed. If the user now hits enter, we assume + // he simply hasn't released the key, rather than that + // he intended to hit "ctrl-enter". + }; + + enum PasteState { + NONE, // Most recent edit was not a paste that replaced all text. + REPLACED_ALL, // Most recent edit was a paste that replaced all text. + REPLACING_ALL, // In the middle of doing a paste that replaces all + // text. We need this intermediate state because OnPaste() + // does the actual detection of such pastes, but + // OnAfterPossibleChange() has to update the paste state + // for every edit. If OnPaste() set the state directly to + // REPLACED_ALL, OnAfterPossibleChange() wouldn't know + // whether that represented the current edit or a past one. + }; + + // Called whenever user_text_ should change. + void InternalSetUserText(const std::wstring& text); + + // Conversion between user text and display text. User text is the text the + // user has input. Display text is the text being shown in the edit. The + // two are different if a keyword is selected. + std::wstring DisplayTextFromUserText(const std::wstring& text) const; + std::wstring UserTextFromDisplayText(const std::wstring& text) const; // Returns the URL. If the user has not edited the text, this returns the // permanent text. If the user has edited the text, this returns the default @@ -155,26 +328,167 @@ class AutocompleteEdit bool* is_history_what_you_typed_match, std::wstring* alternate_nav_url); - // The user text is the text the user has manually keyed in. When present, - // this is shown in preference to the permanent text; hitting escape will - // revert to the permanent text. - void SetUserText(const std::wstring& text) { SetUserText(text, text, true); } + AutocompleteEditView* view_; - // Selects all the text in the edit. Use this in place of SetSelAll() to - // avoid selecting the "phantom newline" at the end of the edit. - void SelectAll(bool reversed); + AutocompletePopupModel* popup_; - // Reverts the edit and popup back to their unedited state (permanent text - // showing, popup closed, no user input in progress). - void RevertAll(); + AutocompleteEditController* controller_; - // Asks the browser to load the popup's currently selected item, using the - // supplied disposition. This may close the popup. If |for_drop| is true, - // it indicates the input is being accepted as part of a drop operation and - // the transition should be treated as LINK (so that it won't trigger the - // URL to be autocompleted). - void AcceptInput(WindowOpenDisposition disposition, - bool for_drop); + // Whether the edit has focus. + bool has_focus_; + + // The URL of the currently displayed page. + std::wstring permanent_text_; + + // This flag is true when the user has modified the contents of the edit, but + // not yet accepted them. We use this to determine when we need to save + // state (on switching tabs) and whether changes to the page URL should be + // immediately displayed. + // This flag will be true in a superset of the cases where the popup is open. + bool user_input_in_progress_; + + // The text that the user has entered. This does not include inline + // autocomplete text that has not yet been accepted. + std::wstring user_text_; + + // When the user closes the popup, we need to remember the URL for their + // desired choice, so that if they hit enter without reopening the popup we + // know where to go. We could simply rerun autocomplete in this case, but + // we'd need to either wait for all results to come in (unacceptably slow) or + // do the wrong thing when the user had chosen some provider whose results + // were not returned instantaneously. + // + // This variable is only valid when user_input_in_progress_ is true, since + // when it is false the user has either never input anything (so there won't + // be a value here anyway) or has canceled their input, which should be + // treated the same way. Also, since this is for preserving a desired URL + // after the popup has been closed, we ignore this if the popup is open, and + // simply ask the popup for the desired URL directly. As a result, the + // contents of this variable only need to be updated when the popup is closed + // but user_input_in_progress_ is not being cleared. + std::wstring url_for_remembered_user_selection_; + + // Inline autocomplete is allowed if the user has not just deleted text, and + // no temporary text is showing. In this case, inline_autocomplete_text_ is + // appended to the user_text_ and displayed selected (at least initially). + // + // NOTE: When the popup is closed there should never be inline autocomplete + // text (actions that close the popup should either accept the text, convert + // it to a normal selection, or change the edit entirely). + bool just_deleted_text_; + std::wstring inline_autocomplete_text_; + + // Used by OnPopupDataChanged to keep track of whether there is currently a + // temporary text. + // + // Example of use: If the user types "goog", then arrows down in the + // autocomplete popup until, say, "google.com" appears in the edit box, then + // the user_text_ is still "goog", and "google.com" is "temporary text". + // When the user hits , the edit box reverts to "goog". Hit again + // and the popup is closed and "goog" is replaced by the permanent_text_, + // which is the URL of the current page. + // + // original_url_ is only valid when there is temporary text, and is used as + // the unique identifier of the originally selected item. Thus, if the user + // arrows to a different item with the same text, we can still distinguish + // them and not revert all the way to the permanent_text_. + // + // original_selected_match_, which is valid in the same cases, is the manually + // selected match to revert the popup to, if any. This can be non-empty when + // the user has selected a keyword (by hitting when applicable), or when + // the user has manually selected a match and then continued to edit it. + bool has_temporary_text_; + std::wstring original_url_; + AutocompleteResult::Selection original_selected_match_; + + // When the user's last action was to paste and replace all the text, we + // disallow inline autocomplete (on the theory that the user is trying to + // paste in a new URL or part of one, and in either case inline autocomplete + // would get in the way). + PasteState paste_state_; + + // Whether the control key is depressed. We track this to avoid calling + // UpdatePopup() repeatedly if the user holds down the key, and to know + // whether to trigger "ctrl-enter" behavior. + ControlKeyState control_key_state_; + + // The keyword associated with the current match. The user may have an actual + // selected keyword, or just some input text that looks like a keyword (so we + // can show a hint to press ). This is the keyword in either case; + // is_keyword_hint_ (below) distinguishes the two cases. + std::wstring keyword_; + + // True if the keyword associated with this match is merely a hint, i.e. the + // user hasn't actually selected a keyword yet. When this is true, we can use + // keyword_ to show a "Press to search" sort of hint. + bool is_keyword_hint_; + + // In some cases, such as when the user is editing in the middle of the input + // string, the input might look like a keyword, but we don't want to display + // the keyword UI, so as not to interfere with the user's editing. + bool disable_keyword_ui_; + + // True when it's safe to show a "Type to search" hint to the user (when the + // edit is empty, or the user is in the process of searching). + bool show_search_hint_; + + // Paste And Go-related state. See CanPasteAndGo(). + mutable std::wstring paste_and_go_url_; + mutable PageTransition::Type paste_and_go_transition_; + mutable std::wstring paste_and_go_alternate_nav_url_; + + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(AutocompleteEditModel); +}; + +// Provides the implementation of an edit control with a drop-down +// autocomplete box. The box itself is implemented in autocomplete_popup.cc +// This file implements the edit box and management for the popup. +class AutocompleteEditView + : public CWindowImpl >, + public CRichEditCommands, + public Menu::Delegate { + public: + struct State { + State(const CHARRANGE& selection, + const CHARRANGE& saved_selection_for_focus_change) + : selection(selection), + saved_selection_for_focus_change(saved_selection_for_focus_change) { + } + + const CHARRANGE selection; + const CHARRANGE saved_selection_for_focus_change; + }; + + DECLARE_WND_CLASS(L"Chrome_AutocompleteEditView"); + + AutocompleteEditView(const ChromeFont& font, + AutocompleteEditController* controller, + ToolbarModel* toolbar_model, + ChromeViews::View* parent_view, + HWND hwnd, + Profile* profile, + CommandController* command_controller, + bool popup_window_mode); + ~AutocompleteEditView(); + + AutocompleteEditModel* model() { return model_.get(); } + const AutocompleteEditModel* model() const { return model_.get(); } + + ChromeViews::View* parent_view() const { return parent_view_; } + + // For use when switching tabs, this saves the current state onto the tab so + // that it can be restored during a later call to Update(). + void SaveStateToTab(TabContents* tab); + + // Called when any LocationBarView state changes. If + // |tab_for_state_restoring| is non-NULL, it points to a TabContents whose + // state we should restore. + void Update(const TabContents* tab_for_state_restoring); // Asks the browser to load the specified URL, which is assumed to be one of // the popup entries, using the supplied disposition and transition type. @@ -193,34 +507,36 @@ class AutocompleteEdit size_t selected_line, const std::wstring& keyword); - // Closes the autocomplete popup, if it's open. - void ClosePopup(); + // Returns the current text of the edit control, which could be the + // "temporary" text set by the popup, the "permanent" text set by the + // browser, or just whatever the user has currently typed. + std::wstring GetText() const; - // Accessors for keyword-related state (see comments on keyword_ and - // is_keyword_hint_). - std::wstring keyword() const { - return ((is_keyword_hint_ && has_focus_) || - (!is_keyword_hint_ && !disable_keyword_ui_)) ? - keyword_ : std::wstring(); - } - bool is_keyword_hint() const { return is_keyword_hint_; } + // The user text is the text the user has manually keyed in. When present, + // this is shown in preference to the permanent text; hitting escape will + // revert to the permanent text. + void SetUserText(const std::wstring& text) { SetUserText(text, text, true); } + void SetUserText(const std::wstring& text, + const std::wstring& display_text, + bool update_popup); - // True if we should show the "Type to search" hint (see comments on - // show_search_hint_). - bool show_search_hint() const { return has_focus_ && show_search_hint_; } + // Sets the window text and the caret position. + void SetWindowTextAndCaretPos(const std::wstring& text, size_t caret_pos); - ChromeViews::View* parent_view() const { return parent_view_; } + // Selects all the text in the edit. Use this in place of SetSelAll() to + // avoid selecting the "phantom newline" at the end of the edit. + void SelectAll(bool reversed); - // Returns true if a query to an autocomplete provider is currently - // in progress. This logic should in the future live in - // AutocompleteController but resides here for now. This method is used by - // AutomationProvider::AutocompleteEditIsQueryInProgress. - bool query_in_progress() const; + // Reverts the edit and popup back to their unedited state (permanent text + // showing, popup closed, no user input in progress). + void RevertAll(); - // Returns the lastest autocomplete results. This logic should in the future - // live in AutocompleteController but resides here for now. This method is - // used by AutomationProvider::AutocompleteEditGetMatches. - const AutocompleteResult* latest_result() const; + // Updates the autocomplete popup and other state after the text has been + // changed by the user. + void UpdatePopup(); + + // Closes the autocomplete popup, if it's open. + void ClosePopup(); // Exposes custom IAccessible implementation to the overall MSAA hierarchy. IAccessible* GetIAccessible(); @@ -237,6 +553,32 @@ class AutocompleteEdit // Inserts the text at the specified position. void InsertText(int position, const std::wstring& text); + // Called when the temporary text in the model may have changed. + // |display_text| is the new text to show; |save_original_selection| is true + // when there wasn't previously a temporary text and thus we need to save off + // the user's existing selection. + void OnTemporaryTextMaybeChanged(const std::wstring& display_text, + bool save_original_selection); + + // Called when the inline autocomplete text in the model may have changed. + // |display_text| is the new text to show; |user_text_length| is the length of + // the user input portion of that (so, up to but not including the inline + // autocompletion). Returns whether the display text actually changed. + bool OnInlineAutocompleteTextMaybeChanged(const std::wstring& display_text, + size_t user_text_length); + + // Called when the temporary text has been reverted by the user. |text| is + // the text that should now be displayed. + void OnRevertTemporaryText(const std::wstring& text); + + // Every piece of code that can change the edit should call these functions + // before and after the change. These functions determine if anything + // meaningful changed, and do any necessary updating and notification. + void OnBeforePossibleChange(); + // OnAfterPossibleChange() returns true if there was a change that caused it + // to call UpdatePopup(). + bool OnAfterPossibleChange(); + // Invokes CanPasteAndGo with the specified text, and if successful navigates // to the appropriate URL. The behavior of this is the same as if the user // typed in the specified text and pressed enter. @@ -250,30 +592,6 @@ class AutocompleteEdit // send us events that we should treat as if they were events on us. void HandleExternalMsg(UINT msg, UINT flags, const CPoint& screen_point); - // Called back by the AutocompletePopupModel when any relevant data changes. - // This rolls together several separate pieces of data into one call so we can - // update all the UI efficiently: - // |text| is either the new temporary text (if |is_temporary_text| is true) - // from the user manually selecting a different match, or the inline - // autocomplete text (if |is_temporary_text| is false). - // |previous_selected_match| is only used when changing the temporary text; - // it is the match that was (manually or automatically) selected before - // the current manual selection, and is saved to be restored later if the - // user hits . - // |can_show_search_hint| is true if the current choice is nonexistent or a - // search result; in these cases it may be OK to show the "Type to search" - // hint (see comments on show_search_hint_). - // |keyword| is the keyword to show a hint for if |is_keyword_hint| is true, - // or the currently selected keyword if |is_keyword_hint| is false (see - // comments on keyword_ and is_keyword_hint_). - void OnPopupDataChanged( - const std::wstring& text, - bool is_temporary_text, - const AutocompleteResult::Selection& previous_selected_match, - const std::wstring& keyword, - bool is_keyword_hint, - bool can_show_search_hint); - // CWindowImpl BEGIN_MSG_MAP(AutocompleteEdit) MSG_WM_CHAR(OnChar) @@ -318,14 +636,14 @@ class AutocompleteEdit // will unfreeze once both freezes are released (the freezes stack). class ScopedFreeze { public: - ScopedFreeze(AutocompleteEdit* edit, ITextDocument* text_object_model); + ScopedFreeze(AutocompleteEditView* edit, ITextDocument* text_object_model); ~ScopedFreeze(); private: - AutocompleteEdit* const edit_; + AutocompleteEditView* const edit_; ITextDocument* const text_object_model_; - DISALLOW_EVIL_CONSTRUCTORS(ScopedFreeze); + DISALLOW_COPY_AND_ASSIGN(ScopedFreeze); }; // This object suspends placing any operations on the edit's undo stack until @@ -340,33 +658,7 @@ class AutocompleteEdit private: ITextDocument* const text_object_model_; - DISALLOW_EVIL_CONSTRUCTORS(ScopedSuspendUndo); - }; - - enum ControlKeyState { - UP, // The control key is not depressed. - DOWN_WITHOUT_CHANGE, // The control key is depressed, and the edit's - // contents/selection have not changed since it was - // depressed. This is the only state in which we - // do the "ctrl-enter" behavior when the user hits - // enter. - DOWN_WITH_CHANGE, // The control key is depressed, and the edit's - // contents/selection have changed since it was - // depressed. If the user now hits enter, we assume - // he simply hasn't released the key, rather than that - // he intended to hit "ctrl-enter". - }; - - enum PasteState { - NONE, // Most recent edit was not a paste that replaced all text. - REPLACED_ALL, // Most recent edit was a paste that replaced all text. - REPLACING_ALL, // In the middle of doing a paste that replaces all - // text. We need this intermediate state because OnPaste() - // does the actual detection of such pastes, but - // OnAfterPossibleChange() has to update the paste state - // for every edit. If OnPaste() set the state directly to - // REPLACED_ALL, OnAfterPossibleChange() wouldn't know - // whether that represented the current edit or a past one. + DISALLOW_COPY_AND_ASSIGN(ScopedSuspendUndo); }; // Replacement word-breaking proc for the rich edit control. @@ -414,39 +706,6 @@ class AutocompleteEdit bool OnKeyDownOnlyWritable(TCHAR key, UINT repeat_count, UINT flags); bool OnKeyDownAllModes(TCHAR key, UINT repeat_count, UINT flags); - // Every piece of code that can change the edit should call these functions - // before and after the change. These functions determine if anything - // meaningful changed, and do any necessary updating and notification. - void OnBeforePossibleChange(); - // OnAfterPossibleChange() returns true if there was a change that caused it - // to call UpdatePopup(). - bool OnAfterPossibleChange(); - - // Implementation for SetUserText(wstring). SetUserText(wstring) invokes this - // with |update_popup| set to false. - void SetUserText(const std::wstring& text, const std::wstring& display_text, - bool update_popup); - - // The inline autocomplete text is shown, selected, appended to the user's - // current input. For example, if the user types in "go", and the - // autocomplete system determines that the best match is "google", this - // routine might be called with |text| = "ogle". - // Returns true if the text in the edit changed. - bool SetInlineAutocompleteText(const std::wstring& text); - - // The temporary text is set when the user arrows around the autocomplete - // popup. When present, this is shown in preference to the user text; - // hitting escape will revert to the user text. - // |previous_selected_match| is used to update the member variable - // |original_selected_match_| if there wasn't already any temporary text. - // Returns true if the text in the edit changed. - bool SetTemporaryText( - const std::wstring& text, - const AutocompleteResult::Selection& previous_selected_match); - - // Called whenever user_text_ should change. - void InternalSetUserText(const std::wstring& text); - // Like GetSel(), but returns a range where |cpMin| will be larger than // |cpMax| if the cursor is at the start rather than the end of the selection // (in other words, tracks selection direction as well as offsets). @@ -467,7 +726,7 @@ class AutocompleteEdit } // Places the caret at the given position. This clears any selection. - void PlaceCaretAt(std::wstring::size_type pos); + void PlaceCaretAt(size_t pos); // Returns true if |sel| represents a forward or backward selection of all the // text. @@ -506,51 +765,17 @@ class AutocompleteEdit const CRect& client_rect, const CRect& paint_clip_rect); - // When the user is pressing the control key, we interpret this as requesting - // us to add the top-level domain "com" to the contents of the edit control. - // Some users expect this behavior because it is present in other browsers. - std::wstring GetDesiredTLD() const; - - // Updates the autocomplete popup and other state after the text has been - // changed by the user. - void UpdatePopup(); - // Internally invoked whenever the text changes in some way. void TextChanged(); - // Conversion between user text and display text. User text is the text the - // user has input. Display text is the text being shown in the edit. The - // two are different if a keyword is selected. - std::wstring DisplayTextFromUserText(const std::wstring& text) const; - std::wstring UserTextFromDisplayText(const std::wstring& text) const; - // Returns the current clipboard contents as a string that can be pasted in. // In addition to just getting CF_UNICODETEXT out, this can also extract URLs // from bookmarks on the clipboard. - std::wstring AutocompleteEdit::GetClipboardText() const; + std::wstring GetClipboardText() const; // Determines whether the user can "paste and go", given the specified text. - // This also updates the internal paste-and-go-related state variables as - // appropriate so that the controller doesn't need to be repeatedly queried - // for the same text in every clipboard-related function. bool CanPasteAndGo(const std::wstring& text) const; - // Navigates to the destination last supplied to CanPasteAndGo. - void PasteAndGo(); - - // Sets the state of user_input_in_progress_, and notifies the observer if - // that state has changed. - void SetInputInProgress(bool in_progress); - - // As necessary, sends out notification that the user is accepting a URL in - // the edit. If the accepted URL is from selecting a keyword, |keyword| is - // the selected keyword. - // If |selected_line| is kNoMatch, the currently selected line is used for the - // metrics log record; otherwise, the provided value is used as the selected - // line. This is used when the user opens a URL without actually selecting - // its entry, such as middle-clicking it. - void SendOpenNotification(size_t selected_line, const std::wstring& keyword); - // Getter for the text_object_model_, used by the ScopedXXX classes. Note // that the pointer returned here is only valid as long as the // AutocompleteEdit is still alive. @@ -573,18 +798,26 @@ class AutocompleteEdit // text. void RepaintDropHighlight(int position); - Controller* controller_; + scoped_ptr model_; + + scoped_ptr popup_model_; - // The Popup itself. - scoped_ptr popup_; + AutocompleteEditController* controller_; + + // The parent view for the edit, used to align the popup and for + // accessibility. + ChromeViews::View* parent_view_; + + ToolbarModel* toolbar_model_; + + // The object that handles additional command functionality exposed on the + // edit, such as invoking the keyword editor. + CommandController* command_controller_; // When true, the location bar view is read only and also is has a slightly // different presentation (font size / color). This is used for popups. bool popup_window_mode_; - // Whether the edit has focus. - bool has_focus_; - // Non-null when the edit is gaining focus from a left click. This is only // needed between when WM_MOUSEACTIVATE and WM_LBUTTONDOWN get processed. It // serves two purposes: first, by communicating to OnLButtonDown() that we're @@ -596,79 +829,6 @@ class AutocompleteEdit // really is shortly afterward. scoped_ptr gaining_focus_; - // The URL of the currently displayed page. - std::wstring permanent_text_; - - // This flag is true when the user has modified the contents of the edit, but - // not yet accepted them. We use this to determine when we need to save - // state (on switching tabs) and whether changes to the page URL should be - // immediately displayed. - // This flag will be true in a superset of the cases where the popup is open. - bool user_input_in_progress_; - - // The text that the user has entered. This does not include inline - // autocomplete text that has not yet been accepted. - std::wstring user_text_; - - // When the user closes the popup, we need to remember the URL for their - // desired choice, so that if they hit enter without reopening the popup we - // know where to go. We could simply rerun autocomplete in this case, but - // we'd need to either wait for all results to come in (unacceptably slow) or - // do the wrong thing when the user had chosen some provider whose results - // were not returned instantaneously. - // - // This variable is only valid when user_input_in_progress_ is true, since - // when it is false the user has either never input anything (so there won't - // be a value here anyway) or has canceled their input, which should be - // treated the same way. Also, since this is for preserving a desired URL - // after the popup has been closed, we ignore this if the popup is open, and - // simply ask the popup for the desired URL directly. As a result, the - // contents of this variable only need to be updated when the popup is closed - // but user_input_in_progress_ is not being cleared. - std::wstring url_for_remembered_user_selection_; - - // Inline autocomplete is allowed if the user has not just deleted text, and - // no temporary text is showing. In this case, inline_autocomplete_text_ is - // appended to the user_text_ and displayed selected (at least initially). - // - // NOTE: When the popup is closed there should never be inline autocomplete - // text (actions that close the popup should either accept the text, convert - // it to a normal selection, or change the edit entirely). - bool just_deleted_text_; - std::wstring inline_autocomplete_text_; - - // Used by Set/RevertTemporaryText to keep track of whether there - // is currently a temporary text. The original_selection_ is only valid when - // there is temporary text. - // - // Example of use: If the user types "goog", then arrows down in the - // autocomplete popup until, say, "google.com" appears in the edit box, then - // the user_text_ is still "goog", and "google.com" is "temporary text". - // When the user hits , the edit box reverts to "goog". Hit again - // and the popup is closed and "goog" is replaced by the permanent_text_, - // which is the URL of the current page. - // - // original_url_ is valid in the same cases as original_selection_, and is - // used as the unique identifier of the originally selected item. Thus, if - // the user arrows to a different item with the same text, we can still - // distinguish them and not revert all the way to the permanent_text_. - // - // original_selected_match_, which is valid in the same cases as the other - // two original_* members, is the manually selected match to revert the popup - // to, if any. This can be non-empty when the user has selected a keyword - // (by hitting when applicable), or when the user has manually selected - // a match and then continued to edit it. - bool has_temporary_text_; - CHARRANGE original_selection_; - std::wstring original_url_; - AutocompleteResult::Selection original_selected_match_; - - // When the user's last action was to paste and replace all the text, we - // disallow inline autocomplete (on the theory that the user is trying to - // paste in a new URL or part of one, and in either case inline autocomplete - // would get in the way). - PasteState paste_state_; - // When the user clicks to give us focus, we watch to see if they're clicking // or dragging. When they're clicking, we select nothing until mouseup, then // select all the text in the edit. During this process, tracking_click_ is @@ -693,6 +853,10 @@ class AutocompleteEdit CHARRANGE sel_before_change_; bool select_all_before_change_; + // Set at the same time the model's original_* members are set, and valid in + // the same cases. + CHARRANGE original_selection_; + // Holds the user's selection across focus changes. cpMin holds -1 when // there is no saved selection. CHARRANGE saved_selection_for_focus_change_; @@ -700,19 +864,6 @@ class AutocompleteEdit // The context menu for the edit. scoped_ptr context_menu_; - // Whether the control key is depressed. We track this to avoid calling - // UpdatePopup() repeatedly if the user holds down the key, and to know - // whether to trigger "ctrl-enter" behavior. - ControlKeyState control_key_state_; - - // The object that handles additional command functionality exposed on the - // edit, such as invoking the keyword editor. - CommandController* command_controller_; - - // The parent view for the edit, used to align the popup and for - // accessibility. - ChromeViews::View* parent_view_; - // Font we're using. We keep a reference to make sure the font supplied to // the constructor doesn't go away before we do. ChromeFont font_; @@ -741,42 +892,13 @@ class AutocompleteEdit // Position of the drop highlight. If this is -1, there is no drop highlight. int drop_highlight_position_; - // The keyword associated with the current match. The user may have an actual - // selected keyword, or just some input text that looks like a keyword (so we - // can show a hint to press ). This is the keyword in either case; - // is_keyword_hint_ (below) distinguishes the two cases. - std::wstring keyword_; - - // True if the keyword associated with this match is merely a hint, i.e. the - // user hasn't actually selected a keyword yet. When this is true, we can use - // keyword_ to show a "Press to search" sort of hint. - bool is_keyword_hint_; - - // In some cases, such as when the user is editing in the middle of the input - // string, the input might look like a keyword, but we don't want to display - // the keyword UI, so as not to interfere with the user's editing. - bool disable_keyword_ui_; - - // True when it's safe to show a "Type to search" hint to the user (when the - // edit is empty, or the user is in the process of searching). - bool show_search_hint_; - // Security UI-related data. COLORREF background_color_; ToolbarModel::SecurityLevel scheme_security_level_; - // Paste And Go-related state. See CanPasteAndGo(). - mutable std::wstring paste_and_go_url_; - mutable PageTransition::Type paste_and_go_transition_; - mutable std::wstring paste_and_go_alternate_nav_url_; - // This interface is useful for accessing the CRichEditCtrl at a low level. mutable CComQIPtr text_object_model_; - ToolbarModel* model_; - - Profile* profile_; - // This contains the scheme char start and stop indexes that should be // striken-out when displaying an insecure scheme. url_parse::Component insecure_scheme_component_; @@ -784,7 +906,22 @@ class AutocompleteEdit // Instance of accessibility information and handling. mutable CComPtr autocomplete_accessibility_; - DISALLOW_COPY_AND_ASSIGN(AutocompleteEdit); + DISALLOW_COPY_AND_ASSIGN(AutocompleteEditView); +}; + +// The AutocompleteEditState struct contains enough information about the +// AutocompleteEditModel and AutocompleteEditView to save/restore a user's +// typing, caret position, etc. across tab changes. We explicitly don't +// preserve things like whether the popup was open as this might be weird. +struct AutocompleteEditState { + AutocompleteEditState(const AutocompleteEditModel::State model_state, + const AutocompleteEditView::State view_state) + : model_state(model_state), + view_state(view_state) { + } + + const AutocompleteEditModel::State model_state; + const AutocompleteEditView::State view_state; }; #endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_H_ diff --git a/chrome/browser/autocomplete/autocomplete_popup.cc b/chrome/browser/autocomplete/autocomplete_popup.cc index ca2dcbc631212a..c7cb50edbd52d5 100644 --- a/chrome/browser/autocomplete/autocomplete_popup.cc +++ b/chrome/browser/autocomplete/autocomplete_popup.cc @@ -134,8 +134,10 @@ int MirroringContext::GetLeft(int x1, int x2) const { const wchar_t AutocompletePopupView::DrawLineInfo::ellipsis_str[] = L"\x2026"; AutocompletePopupView::AutocompletePopupView(AutocompletePopupModel* model, - const ChromeFont& font) + const ChromeFont& font, + AutocompleteEditView* edit_view) : model_(model), + edit_view_(edit_view), line_info_(font), mirroring_context_(new MirroringContext()), star_(ResourceBundle::GetSharedInstance().GetBitmapNamed( @@ -168,12 +170,11 @@ void AutocompletePopupView::UpdatePopupAppearance() { // TODO(pkasting): http://b/1345937 All this use of editor accessors should // die once this class is a true ChromeView. CRect rc; - model_->editor()->parent_view()->GetBounds(&rc); + edit_view_->parent_view()->GetBounds(&rc); // Subtract the top left corner to make the coordinates relative to the // location bar view itself, and convert to screen coordinates. CPoint top_left(-rc.TopLeft()); - ChromeViews::View::ConvertPointToScreen(model_->editor()->parent_view(), - &top_left); + ChromeViews::View::ConvertPointToScreen(edit_view_->parent_view(), &top_left); rc.OffsetRect(top_left); // Expand by one pixel on each side since that's the amount the location bar // view is inset from the divider line that edges the adjacent buttons. @@ -194,14 +195,14 @@ void AutocompletePopupView::UpdatePopupAppearance() { if (!m_hWnd) { // To prevent this window from being activated, we create an invisible // window and manually show it without activating it. - Create(model_->editor()->m_hWnd, rc, AUTOCOMPLETEPOPUPVIEW_CLASSNAME, - WS_POPUP, WS_EX_TOOLWINDOW); + Create(edit_view_->m_hWnd, rc, AUTOCOMPLETEPOPUPVIEW_CLASSNAME, WS_POPUP, + WS_EX_TOOLWINDOW); // When an IME is attached to the rich-edit control, retrieve its window // handle and show this popup window under the IME windows. // Otherwise, show this popup window under top-most windows. // TODO(hbono): http://b/1111369 if we exclude this popup window from the // display area of IME windows, this workaround becomes unnecessary. - HWND ime_window = ImmGetDefaultIMEWnd(model_->editor()->m_hWnd); + HWND ime_window = ImmGetDefaultIMEWnd(edit_view_->m_hWnd); SetWindowPos(ime_window ? ime_window : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW); } else { @@ -217,9 +218,9 @@ void AutocompletePopupView::UpdatePopupAppearance() { } // TODO(pkasting): http://b/1111369 We should call ImmSetCandidateWindow() on - // the model_->editor()'s IME context here, and exclude ourselves from its - // display area. Not clear what to pass for the lpCandidate->ptCurrentPos - // member, though... + // the edit_view_'s IME context here, and exclude ourselves from its display + // area. Not clear what to pass for the lpCandidate->ptCurrentPos member, + // though... } void AutocompletePopupView::OnHoverEnabledOrDisabled(bool disabled) { @@ -333,7 +334,16 @@ void AutocompletePopupView::OnPaint(HDC other_dc) { void AutocompletePopupView::OnButtonUp(const CPoint& point, WindowOpenDisposition disposition) { - model_->OpenLine(PixelToLine(point.y), disposition); + const size_t line = PixelToLine(point.y); + const AutocompleteMatch& match = model_->result()->match_at(line); + // OpenURL() may close the popup, which will clear the result set and, by + // extension, |match| and its contents. So copy the relevant strings out to + // make sure they stay alive until the call completes. + const std::wstring url(match.destination_url); + std::wstring keyword; + const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword); + edit_view_->OpenURL(url, disposition, match.transition, std::wstring(), line, + is_keyword_hint ? std::wstring() : keyword); } int AutocompletePopupView::LineTopPixel(size_t line) const { @@ -712,11 +722,13 @@ const int kPopupCoalesceMs = 100; const int kPopupUpdateMaxDelayMs = 300; }; -AutocompletePopupModel::AutocompletePopupModel(const ChromeFont& font, - AutocompleteEdit* editor, - Profile* profile) - : view_(new AutocompletePopupView(this, font)), - editor_(editor), +AutocompletePopupModel::AutocompletePopupModel( + const ChromeFont& font, + AutocompleteEditView* edit_view, + AutocompleteEditModel* edit_model, + Profile* profile) + : view_(new AutocompletePopupView(this, font, edit_view)), + edit_model_(edit_model), controller_(new AutocompleteController(this, profile)), profile_(profile), query_in_progress_(false), @@ -799,11 +811,13 @@ void AutocompletePopupModel::StopAutocomplete() { latest_result_.Reset(); CommitLatestResults(true); - // Clear input_ to make sure we don't try and use any of these results for - // the next query we receive. Strictly speaking this isn't necessary, since - // the popup isn't open, but it keeps our internal state consistent and - // serves as future-proofing in case the code in StartAutocomplete() changes. + // Clear input_ and manually_selected_match_ to make sure we don't try and use + // any of these results for the next query we receive. Strictly speaking this + // isn't necessary, since the popup isn't open, but it keeps our internal + // state consistent and serves as future-proofing in case the code in + // StartAutocomplete() changes. input_.Clear(); + manually_selected_match_.Clear(); } void AutocompletePopupModel::SetHoveredLine(size_t line) { @@ -840,10 +854,10 @@ void AutocompletePopupModel::SetSelectedLine(size_t line) { const AutocompleteMatch& match = result_.match_at(line); std::wstring keyword; const bool is_keyword_hint = GetKeywordForMatch(match, &keyword); - editor_->OnPopupDataChanged(match.fill_into_edit, true, - manually_selected_match_, keyword, - is_keyword_hint, - (match.type == AutocompleteMatch::SEARCH)); + edit_model_->OnPopupDataChanged(match.fill_into_edit, true, + manually_selected_match_, keyword, + is_keyword_hint, + (match.type == AutocompleteMatch::SEARCH)); // Track the user's selection until they cancel it. manually_selected_match_.destination_url = match.destination_url; @@ -933,6 +947,38 @@ std::wstring AutocompletePopupModel::URLsForDefaultMatch( return url; } +bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match, + std::wstring* keyword) { + // Assume we have no keyword until we find otherwise. + keyword->clear(); + + // If the current match is a keyword, return that as the selected keyword. + if (match.template_url && match.template_url->url() && + match.template_url->url()->SupportsReplacement()) { + keyword->assign(match.template_url->keyword()); + return false; + } + + // See if the current match's fill_into_edit corresponds to a keyword. + if (!profile_->GetTemplateURLModel()) + return false; + profile_->GetTemplateURLModel()->Load(); + const std::wstring keyword_hint( + TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit)); + if (keyword_hint.empty()) + return false; + + // Don't provide a hint if this keyword doesn't support replacement. + const TemplateURL* const template_url = + profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint); + if (!template_url || !template_url->url() || + !template_url->url()->SupportsReplacement()) + return false; + + keyword->assign(keyword_hint); + return true; +} + AutocompleteLog* AutocompletePopupModel::GetAutocompleteLog() { return new AutocompleteLog(input_.text(), selected_line_, 0, result_); } @@ -984,19 +1030,6 @@ void AutocompletePopupModel::TryDeletingCurrentItem() { } } -void AutocompletePopupModel::OpenLine(size_t line, - WindowOpenDisposition disposition) { - const AutocompleteMatch& match = result_.match_at(line); - // OpenURL() may close the popup, which will clear the result set and, by - // extension, |match| and its contents. So copy the relevant strings out to - // make sure they stay alive until the call completes. - const std::wstring url(match.destination_url); - std::wstring keyword; - const bool is_keyword_hint = GetKeywordForMatch(match, &keyword); - editor_->OpenURL(url, disposition, match.transition, std::wstring(), line, - is_keyword_hint ? std::wstring() : keyword); -} - void AutocompletePopupModel::OnAutocompleteUpdate(bool updated_result, bool query_complete) { DCHECK(query_in_progress_); @@ -1055,7 +1088,7 @@ void AutocompletePopupModel::SetDefaultMatchAndUpdate(bool immediately) { is_keyword_hint = GetKeywordForMatch(*match, &keyword); can_show_search_hint = (match->type == AutocompleteMatch::SEARCH); } - editor_->OnPopupDataChanged(inline_autocomplete_text, false, + edit_model_->OnPopupDataChanged(inline_autocomplete_text, false, manually_selected_match_ /* ignored */, keyword, is_keyword_hint, can_show_search_hint); } @@ -1091,35 +1124,3 @@ void AutocompletePopupModel::CommitLatestResults(bool force) { else max_delay_timer_.Stop(); } - -bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match, - std::wstring* keyword) { - // Assume we have no keyword until we find otherwise. - keyword->clear(); - - // If the current match is a keyword, return that as the selected keyword. - if (match.template_url && match.template_url->url() && - match.template_url->url()->SupportsReplacement()) { - keyword->assign(match.template_url->keyword()); - return false; - } - - // See if the current match's fill_into_edit corresponds to a keyword. - if (!profile_->GetTemplateURLModel()) - return false; - profile_->GetTemplateURLModel()->Load(); - const std::wstring keyword_hint( - TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit)); - if (keyword_hint.empty()) - return false; - - // Don't provide a hint if this keyword doesn't support replacement. - const TemplateURL* const template_url = - profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint); - if (!template_url || !template_url->url() || - !template_url->url()->SupportsReplacement()) - return false; - - keyword->assign(keyword_hint); - return true; -} diff --git a/chrome/browser/autocomplete/autocomplete_popup.h b/chrome/browser/autocomplete/autocomplete_popup.h index 160c766f1d9f20..522ca1c7d4ec69 100644 --- a/chrome/browser/autocomplete/autocomplete_popup.h +++ b/chrome/browser/autocomplete/autocomplete_popup.h @@ -17,20 +17,21 @@ #include "chrome/common/gfx/chrome_font.h" #include "chrome/views/view.h" -class AutocompleteEdit; -class AutocompletePopupModel; -class AutocompletePopupView; +class AutocompleteEditModel; +class AutocompleteEditView; class Profile; class MirroringContext; class SkBitmap; +class AutocompletePopupModel; +class AutocompletePopupView; + // TODO(pkasting): http://b/1343512 The names and contents of the classes in // this file are temporary. I am in hack-and-slash mode right now. #define AUTOCOMPLETEPOPUPVIEW_CLASSNAME L"Chrome_AutocompletePopupView" -// This class implements a popup window used by AutocompleteEdit to display -// autocomplete results. +// This class implements a popup window used to display autocomplete results. class AutocompletePopupView : public CWindowImpl { public: @@ -50,7 +51,9 @@ class AutocompletePopupView MSG_WM_PAINT(OnPaint) END_MSG_MAP() - AutocompletePopupView(AutocompletePopupModel* model, const ChromeFont& font); + AutocompletePopupView(AutocompletePopupModel* model, + const ChromeFont& font, + AutocompleteEditView* edit_view); // Returns true if the popup is currently open. bool is_open() const { return m_hWnd != NULL; } @@ -179,6 +182,8 @@ class AutocompletePopupView AutocompletePopupModel* model_; + AutocompleteEditView* edit_view_; + // Cached GDI information for drawing. DrawLineInfo line_info_; @@ -194,12 +199,15 @@ class AutocompletePopupView // re-enabled. When hovered_line_ is a valid line, the value here is // out-of-date and should be ignored. CPoint last_hover_coordinates_; + + DISALLOW_COPY_AND_ASSIGN(AutocompletePopupView); }; class AutocompletePopupModel : public ACControllerListener, public Task { public: AutocompletePopupModel(const ChromeFont& font, - AutocompleteEdit* editor, + AutocompleteEditView* edit_view, + AutocompleteEditModel* edit_model, Profile* profile); ~AutocompletePopupModel(); @@ -225,9 +233,6 @@ class AutocompletePopupModel : public ACControllerListener, public Task { // Returns true if the popup is currently open. bool is_open() const { return view_->is_open(); } - // TODO(pkasting): http://b/134593 This is temporary and should die. - const AutocompleteEdit* editor() const { return editor_; } - // Returns the AutocompleteController used by this popup. AutocompleteController* autocomplete_controller() const { return controller_.get(); @@ -300,6 +305,14 @@ class AutocompletePopupModel : public ACControllerListener, public Task { bool* is_history_what_you_typed_match, std::wstring* alternate_nav_url); + // Gets the selected keyword or keyword hint for the given match. Returns + // true if |keyword| represents a keyword hint, or false if |keyword| + // represents a selected keyword. (|keyword| will always be set [though + // possibly to the empty string], and you cannot have both a selected keyword + // and a keyword hint simultaneously.) + bool GetKeywordForMatch(const AutocompleteMatch& match, + std::wstring* keyword); + // Returns a pointer to a heap-allocated AutocompleteLog containing the // current input text, selected match, and result set. The caller is // responsible for deleting the object. @@ -309,16 +322,13 @@ class AutocompletePopupModel : public ACControllerListener, public Task { // current selection down (|count| > 0) or up (|count| < 0), clamping to the // first or last result if necessary. If |count| == 0, the selection will be // unchanged, but the popup will still redraw and modify the text in the - // AutocompleteEdit. + // AutocompleteEditModel. void Move(int count); // Called when the user hits shift-delete. This should determine if the item // can be removed from history, and if so, remove it and update the popup. void TryDeletingCurrentItem(); - // Called by the view to open the URL corresponding to a particular line. - void OpenLine(size_t line, WindowOpenDisposition disposition); - // ACControllerListener - called when more autocomplete data is available or // when the query is complete. // @@ -347,17 +357,9 @@ class AutocompletePopupModel : public ACControllerListener, public Task { // changes back to the user. void CommitLatestResults(bool force); - // Gets the selected keyword or keyword hint for the given match. Returns - // true if |keyword| represents a keyword hint, or false if |keyword| - // represents a selected keyword. (|keyword| will always be set [though - // possibly to the empty string], and you cannot have both a selected keyword - // and a keyword hint simultaneously.) - bool GetKeywordForMatch(const AutocompleteMatch& match, - std::wstring* keyword); - scoped_ptr view_; - AutocompleteEdit* editor_; + AutocompleteEditModel* edit_model_; scoped_ptr controller_; // Profile for current tab. @@ -399,6 +401,8 @@ class AutocompletePopupModel : public ACControllerListener, public Task { // The currently selected line. This is kNoMatch when nothing is selected, // which should only be true when the popup is closed. size_t selected_line_; + + DISALLOW_COPY_AND_ASSIGN(AutocompletePopupModel); }; #endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_POPUP_H_ diff --git a/chrome/browser/autocomplete/edit_drop_target.cc b/chrome/browser/autocomplete/edit_drop_target.cc index c1365cc0fd87bc..64f824d8a65dc7 100644 --- a/chrome/browser/autocomplete/edit_drop_target.cc +++ b/chrome/browser/autocomplete/edit_drop_target.cc @@ -23,7 +23,7 @@ DWORD CopyOrLinkDropEffect(DWORD effect) { } -EditDropTarget::EditDropTarget(AutocompleteEdit* edit) +EditDropTarget::EditDropTarget(AutocompleteEditView* edit) : BaseDropTarget(edit->m_hWnd), edit_(edit), drag_has_url_(false), @@ -91,7 +91,7 @@ DWORD EditDropTarget::OnDrop(IDataObject* data_object, std::wstring title; if (os_data.GetURLAndTitle(&url, &title)) { edit_->SetUserText(UTF8ToWide(url.spec())); - edit_->AcceptInput(CURRENT_TAB, true); + edit_->model()->AcceptInput(CURRENT_TAB, true); return CopyOrLinkDropEffect(effect); } } else if (drag_has_string_) { diff --git a/chrome/browser/autocomplete/edit_drop_target.h b/chrome/browser/autocomplete/edit_drop_target.h index 354843e005473f..4cd2f00bb1412d 100644 --- a/chrome/browser/autocomplete/edit_drop_target.h +++ b/chrome/browser/autocomplete/edit_drop_target.h @@ -7,17 +7,17 @@ #include "base/base_drop_target.h" -class AutocompleteEdit; +class AutocompleteEditView; // EditDropTarget is the IDropTarget implementation installed on -// AutocompleteEdit. EditDropTarget prefers URL over plain text. A drop of a URL -// replaces all the text of the edit and navigates immediately to the URL. A +// AutocompleteEditView. EditDropTarget prefers URL over plain text. A drop of a +// URL replaces all the text of the edit and navigates immediately to the URL. A // drop of plain text from the same edit either copies or moves the selected // text, and a drop of plain text from a source other than the edit does a paste // and go. class EditDropTarget : public BaseDropTarget { public: - explicit EditDropTarget(AutocompleteEdit* edit); + explicit EditDropTarget(AutocompleteEditView* edit); protected: virtual DWORD OnDragEnter(IDataObject* data_object, @@ -43,7 +43,7 @@ class EditDropTarget : public BaseDropTarget { void ResetDropHighlights(); // The edit we're the drop target for. - AutocompleteEdit* edit_; + AutocompleteEditView* edit_; // If true, the drag session contains a URL. bool drag_has_url_; diff --git a/chrome/browser/automation/automation_autocomplete_edit_tracker.h b/chrome/browser/automation/automation_autocomplete_edit_tracker.h index b750938103331c..81f0ecdee10be8 100644 --- a/chrome/browser/automation/automation_autocomplete_edit_tracker.h +++ b/chrome/browser/automation/automation_autocomplete_edit_tracker.h @@ -9,7 +9,7 @@ #include "chrome/browser/automation/automation_resource_tracker.h" class AutomationAutocompleteEditTracker: - public AutomationResourceTracker { + public AutomationResourceTracker { public: explicit AutomationAutocompleteEditTracker(IPC::Message::Sender* automation) : AutomationResourceTracker(automation) { } @@ -18,16 +18,16 @@ class AutomationAutocompleteEditTracker: ClearAllMappings(); } - virtual void AddObserver(AutocompleteEdit* resource) { + virtual void AddObserver(AutocompleteEditView* resource) { NotificationService::current()->AddObserver( this, NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, - Source(resource)); + Source(resource)); } - virtual void RemoveObserver(AutocompleteEdit* resource) { + virtual void RemoveObserver(AutocompleteEditView* resource) { NotificationService::current()->RemoveObserver( this, NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, - Source(resource)); + Source(resource)); } }; diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index b831a719c41a1f..a9ae6a8b8abd5b 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -1838,10 +1838,9 @@ void AutomationProvider::GetAutocompleteEditForBrowser( if (browser_tracker_->ContainsHandle(browser_handle)) { Browser* browser = browser_tracker_->GetResource(browser_handle); LocationBarView* loc_bar_view = browser->GetLocationBarView(); - AutocompleteEdit* autocomplete_edit = loc_bar_view->location_entry(); + AutocompleteEditView* edit_view = loc_bar_view->location_entry(); // Add() returns the existing handle for the resource if any. - autocomplete_edit_handle = - autocomplete_edit_tracker_->Add(autocomplete_edit); + autocomplete_edit_handle = autocomplete_edit_tracker_->Add(edit_view); success = true; } Send(new AutomationMsg_AutocompleteEditForBrowserResponse( @@ -2159,9 +2158,8 @@ void AutomationProvider::GetAutocompleteEditText(const IPC::Message& message, bool success = false; std::wstring text; if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { - AutocompleteEdit* edit = autocomplete_edit_tracker_->GetResource( - autocomplete_edit_handle); - text = edit->GetText(); + text = autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)-> + GetText(); success = true; } Send(new AutomationMsg_AutocompleteEditGetTextResponse(message.routing_id(), @@ -2173,9 +2171,8 @@ void AutomationProvider::SetAutocompleteEditText(const IPC::Message& message, const std::wstring& text) { bool success = false; if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { - AutocompleteEdit* edit = autocomplete_edit_tracker_->GetResource( - autocomplete_edit_handle); - edit->SetUserText(text); + autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)-> + SetUserText(text); success = true; } Send(new AutomationMsg_AutocompleteEditSetTextResponse( @@ -2188,9 +2185,8 @@ void AutomationProvider::AutocompleteEditGetMatches( bool success = false; std::vector matches; if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { - AutocompleteEdit* edit = - autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle); - const AutocompleteResult* result = edit->latest_result(); + const AutocompleteResult* result = autocomplete_edit_tracker_-> + GetResource(autocomplete_edit_handle)->model()->latest_result(); for (AutocompleteResult::const_iterator i = result->begin(); i != result->end(); ++i) matches.push_back(AutocompleteMatchData(*i)); @@ -2206,9 +2202,8 @@ void AutomationProvider::AutocompleteEditIsQueryInProgress( bool success = false; bool query_in_progress = false; if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) { - AutocompleteEdit* edit = - autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle); - query_in_progress = edit->query_in_progress(); + query_in_progress = autocomplete_edit_tracker_-> + GetResource(autocomplete_edit_handle)->model()->query_in_progress(); success = true; } Send(new AutomationMsg_AutocompleteEditIsQueryInProgressResponse( diff --git a/chrome/browser/browser_commands.cc b/chrome/browser/browser_commands.cc index 052187d3fceced..6fe35937dfaf35 100644 --- a/chrome/browser/browser_commands.cc +++ b/chrome/browser/browser_commands.cc @@ -280,7 +280,7 @@ void Browser::ExecuteCommand(int id) { { LocationBarView* lbv = GetLocationBarView(); if (lbv) - lbv->location_entry()->AcceptInput(CURRENT_TAB, false); + lbv->location_entry()->model()->AcceptInput(CURRENT_TAB, false); } break; @@ -323,9 +323,9 @@ void Browser::ExecuteCommand(int id) { { LocationBarView* lbv = GetLocationBarView(); if (lbv) { - AutocompleteEdit* ae = lbv->location_entry(); - ae->SetFocus(); - ae->SelectAll(true); + AutocompleteEditView* aev = lbv->location_entry(); + aev->SetFocus(); + aev->SelectAll(true); } } break; @@ -335,9 +335,9 @@ void Browser::ExecuteCommand(int id) { { LocationBarView* lbv = GetLocationBarView(); if (lbv) { - AutocompleteEdit* ae = lbv->location_entry(); - ae->SetUserText(L"?"); - ae->SetFocus(); + AutocompleteEditView* aev = lbv->location_entry(); + aev->model()->SetUserText(L"?"); + aev->SetFocus(); } } break; diff --git a/chrome/browser/tab_contents.h b/chrome/browser/tab_contents.h index 649de9f5a0638e..b079eeb1c2533d 100644 --- a/chrome/browser/tab_contents.h +++ b/chrome/browser/tab_contents.h @@ -181,10 +181,10 @@ class TabContents : public PageNavigator, // For use when switching tabs, these functions allow the tab contents to // hold the per-tab state of the location bar. The tab contents takes // ownership of the pointer. - void set_saved_location_bar_state(const AutocompleteEdit::State* state) { + void set_saved_location_bar_state(const AutocompleteEditState* state) { saved_location_bar_state_.reset(state); } - const AutocompleteEdit::State* saved_location_bar_state() const { + const AutocompleteEditState* saved_location_bar_state() const { return saved_location_bar_state_.get(); } @@ -524,7 +524,7 @@ class TabContents : public PageNavigator, TabContentsDelegate* delegate_; NavigationController* controller_; - scoped_ptr saved_location_bar_state_; + scoped_ptr saved_location_bar_state_; // The download shelf view (view at the bottom of the page). scoped_ptr download_shelf_view_; diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc index e9c0837298b930..ce9da678df045f 100644 --- a/chrome/browser/views/location_bar_view.cc +++ b/chrome/browser/views/location_bar_view.cc @@ -125,10 +125,10 @@ void LocationBarView::Init() { // URL edit field. ChromeViews::ViewContainer* vc = GetViewContainer(); DCHECK(vc) << "LocationBarView::Init - vc is NULL!"; - location_entry_.reset(new AutocompleteEdit(font_, this, model_, this, - vc->GetHWND(), - profile_, controller_, - popup_window_mode_)); + location_entry_.reset(new AutocompleteEditView(font_, this, model_, this, + vc->GetHWND(), + profile_, controller_, + popup_window_mode_)); // View container for URL edit field. location_entry_view_ = new ChromeViews::HWNDView; @@ -168,7 +168,7 @@ void LocationBarView::Init() { info_label_.SetParentOwned(false); // Notify us when any ancestor is resized. In this case we want to tell the - // AutocompleteEdit to close its popup. + // AutocompleteEditView to close its popup. SetNotifyWhenVisibleBoundsInRootChanges(true); // Initialize the location entry. We do this to avoid a black flash which is @@ -197,7 +197,7 @@ void LocationBarView::SetProfile(Profile* profile) { DCHECK(profile); if (profile_ != profile) { profile_ = profile; - location_entry_->SetProfile(profile); + location_entry_->model()->SetProfile(profile); selected_keyword_view_.set_profile(profile); keyword_hint_view_.set_profile(profile); security_image_view_.set_profile(profile); @@ -463,11 +463,11 @@ bool LocationBarView::NeedsResize(View* view, int text_width, int max_width) { } bool LocationBarView::AdjustHints(int text_width, int max_width) { - const std::wstring keyword(location_entry_->keyword()); - const bool is_keyword_hint(location_entry_->is_keyword_hint()); + const std::wstring keyword(location_entry_->model()->keyword()); + const bool is_keyword_hint(location_entry_->model()->is_keyword_hint()); const bool show_selected_keyword = !keyword.empty() && !is_keyword_hint; const bool show_keyword_hint = !keyword.empty() && is_keyword_hint; - bool show_search_hint(location_entry_->show_search_hint()); + bool show_search_hint(location_entry_->model()->show_search_hint()); DCHECK(keyword.empty() || !show_search_hint); if (show_search_hint) { diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h index 3631ac8464b5e9..f02c24d0963025 100644 --- a/chrome/browser/views/location_bar_view.h +++ b/chrome/browser/views/location_bar_view.h @@ -31,7 +31,7 @@ class Profile; // ///////////////////////////////////////////////////////////////////////////// class LocationBarView : public ChromeViews::View, - public AutocompleteEdit::Controller { + public AutocompleteEditController { public: class Delegate { @@ -80,8 +80,8 @@ class LocationBarView : public ChromeViews::View, // Overridden from View so we can use to go into keyword search mode. virtual bool CanProcessTabKeyEvents(); - // Called when any ancestor changes its size, asks the AutocompleteEdit to - // close its popup. + // Called when any ancestor changes its size, asks the AutocompleteEditModel + // to close its popup. virtual void VisibleBoundsInRootChanged(); // Event Handlers @@ -90,7 +90,7 @@ class LocationBarView : public ChromeViews::View, virtual void OnMouseReleased(const ChromeViews::MouseEvent& event, bool canceled); - // AutocompleteEdit::Controller + // AutocompleteEditController virtual void OnAutocompleteAccept(const std::wstring& url, WindowOpenDisposition disposition, PageTransition::Type transition, @@ -105,7 +105,7 @@ class LocationBarView : public ChromeViews::View, // Returns the MSAA role bool GetAccessibleRole(VARIANT* role); - AutocompleteEdit* location_entry() { + AutocompleteEditView* location_entry() { return location_entry_.get(); } @@ -126,7 +126,7 @@ class LocationBarView : public ChromeViews::View, void ShowFirstRunBubble(); // Overridden from View. - virtual bool OverrideAccelerator(const ChromeViews::Accelerator& accelerator) ; + virtual bool OverrideAccelerator(const ChromeViews::Accelerator& accelerator); static const int kTextVertMargin; static const COLORREF kBackgroundColorByLevel[]; @@ -341,7 +341,7 @@ class LocationBarView : public ChromeViews::View, Profile* profile_; // The Autocomplete Edit field. - scoped_ptr location_entry_; + scoped_ptr location_entry_; // The command controller for this View. CommandController* controller_; diff --git a/chrome/views/accessibility/autocomplete_accessibility.cc b/chrome/views/accessibility/autocomplete_accessibility.cc index 62670b441d8c78..4318a4aed09f06 100644 --- a/chrome/views/accessibility/autocomplete_accessibility.cc +++ b/chrome/views/accessibility/autocomplete_accessibility.cc @@ -11,7 +11,7 @@ #include "generated_resources.h" HRESULT AutocompleteAccessibility::Initialize( - const AutocompleteEdit* edit_box) { + const AutocompleteEditView* edit_box) { if (edit_box == NULL) { return E_INVALIDARG; } diff --git a/chrome/views/accessibility/autocomplete_accessibility.h b/chrome/views/accessibility/autocomplete_accessibility.h index c72ca1eb33b905..847e25164b9bb9 100644 --- a/chrome/views/accessibility/autocomplete_accessibility.h +++ b/chrome/views/accessibility/autocomplete_accessibility.h @@ -17,9 +17,9 @@ // // AutocompleteAccessibility // -// Class implementing the MSAA IAccessible COM interface for AutocompleteEdit, -// providing accessibility to be used by screen readers and other assistive -// technology (AT). +// Class implementing the MSAA IAccessible COM interface for +// AutocompleteEditView, providing accessibility to be used by screen readers +// and other assistive technology (AT). // //////////////////////////////////////////////////////////////////////////////// class ATL_NO_VTABLE AutocompleteAccessibility @@ -34,7 +34,7 @@ class ATL_NO_VTABLE AutocompleteAccessibility AutocompleteAccessibility() {} ~AutocompleteAccessibility() {} - HRESULT Initialize(const AutocompleteEdit* edit_box); + HRESULT Initialize(const AutocompleteEditView* edit_box); // Supported IAccessible methods. @@ -106,7 +106,7 @@ class ATL_NO_VTABLE AutocompleteAccessibility CComPtr default_accessibility_server_; private: - const AutocompleteEdit* edit_box_; + const AutocompleteEditView* edit_box_; DISALLOW_EVIL_CONSTRUCTORS(AutocompleteAccessibility); };