| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" | 5 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "unicode/ubidi.h" | 9 #include "unicode/ubidi.h" |
| 10 | 10 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 | 22 |
| 23 /////////////////////////////////////////////////////////////////////////////// | 23 /////////////////////////////////////////////////////////////////////////////// |
| 24 // AutocompletePopupModel | 24 // AutocompletePopupModel |
| 25 | 25 |
| 26 AutocompletePopupModel::AutocompletePopupModel( | 26 AutocompletePopupModel::AutocompletePopupModel( |
| 27 AutocompletePopupView* popup_view, | 27 AutocompletePopupView* popup_view, |
| 28 AutocompleteEditModel* edit_model) | 28 AutocompleteEditModel* edit_model) |
| 29 : view_(popup_view), | 29 : view_(popup_view), |
| 30 edit_model_(edit_model), | 30 edit_model_(edit_model), |
| 31 hovered_line_(kNoMatch), | 31 hovered_line_(kNoMatch), |
| 32 selected_line_(kNoMatch) { | 32 selected_line_(kNoMatch), |
| 33 selected_line_state_(NORMAL) { |
| 33 edit_model->set_popup_model(this); | 34 edit_model->set_popup_model(this); |
| 34 } | 35 } |
| 35 | 36 |
| 36 AutocompletePopupModel::~AutocompletePopupModel() { | 37 AutocompletePopupModel::~AutocompletePopupModel() { |
| 37 } | 38 } |
| 38 | 39 |
| 39 bool AutocompletePopupModel::IsOpen() const { | 40 bool AutocompletePopupModel::IsOpen() const { |
| 40 return view_->IsOpen(); | 41 return view_->IsOpen(); |
| 41 } | 42 } |
| 42 | 43 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 // Track the user's selection until they cancel it. | 77 // Track the user's selection until they cancel it. |
| 77 manually_selected_match_.destination_url = match.destination_url; | 78 manually_selected_match_.destination_url = match.destination_url; |
| 78 manually_selected_match_.provider_affinity = match.provider; | 79 manually_selected_match_.provider_affinity = match.provider; |
| 79 manually_selected_match_.is_history_what_you_typed_match = | 80 manually_selected_match_.is_history_what_you_typed_match = |
| 80 match.is_history_what_you_typed_match; | 81 match.is_history_what_you_typed_match; |
| 81 } | 82 } |
| 82 | 83 |
| 83 if (line == selected_line_ && !force) | 84 if (line == selected_line_ && !force) |
| 84 return; // Nothing else to do. | 85 return; // Nothing else to do. |
| 85 | 86 |
| 86 // We need to update |selected_line_| before calling OnPopupDataChanged(), so | 87 // We need to update |selected_line_state_| and |selected_line_| before |
| 87 // that when the edit notifies its controller that something has changed, the | 88 // calling InvalidateLine(), since it will check them to determine how to |
| 88 // controller can get the correct updated data. | 89 // draw. We also need to update |selected_line_| before calling |
| 90 // OnPopupDataChanged(), so that when the edit notifies its controller that |
| 91 // something has changed, the controller can get the correct updated data. |
| 89 // | 92 // |
| 90 // NOTE: We should never reach here with no selected line; the same code that | 93 // NOTE: We should never reach here with no selected line; the same code that |
| 91 // opened the popup and made it possible to get here should have also set a | 94 // opened the popup and made it possible to get here should have also set a |
| 92 // selected line. | 95 // selected line. |
| 93 CHECK(selected_line_ != kNoMatch); | 96 CHECK(selected_line_ != kNoMatch); |
| 94 GURL current_destination(result.match_at(selected_line_).destination_url); | 97 GURL current_destination(result.match_at(selected_line_).destination_url); |
| 95 view_->InvalidateLine(selected_line_); | 98 const size_t prev_selected_line = selected_line_; |
| 99 selected_line_state_ = NORMAL; |
| 96 selected_line_ = line; | 100 selected_line_ = line; |
| 101 view_->InvalidateLine(prev_selected_line); |
| 97 view_->InvalidateLine(selected_line_); | 102 view_->InvalidateLine(selected_line_); |
| 98 | 103 |
| 99 // Update the edit with the new data for this match. | 104 // Update the edit with the new data for this match. |
| 100 // TODO(pkasting): If |selected_line_| moves to the controller, this can be | 105 // TODO(pkasting): If |selected_line_| moves to the controller, this can be |
| 101 // eliminated and just become a call to the observer on the edit. | 106 // eliminated and just become a call to the observer on the edit. |
| 102 string16 keyword; | 107 string16 keyword; |
| 103 const bool is_keyword_hint = GetKeywordForMatch(match, &keyword); | 108 const bool is_keyword_hint = match.GetKeyword(&keyword); |
| 109 |
| 104 if (reset_to_default) { | 110 if (reset_to_default) { |
| 105 string16 inline_autocomplete_text; | 111 string16 inline_autocomplete_text; |
| 106 if ((match.inline_autocomplete_offset != string16::npos) && | 112 if ((match.inline_autocomplete_offset != string16::npos) && |
| 107 (match.inline_autocomplete_offset < match.fill_into_edit.length())) { | 113 (match.inline_autocomplete_offset < match.fill_into_edit.length())) { |
| 108 inline_autocomplete_text = | 114 inline_autocomplete_text = |
| 109 match.fill_into_edit.substr(match.inline_autocomplete_offset); | 115 match.fill_into_edit.substr(match.inline_autocomplete_offset); |
| 110 } | 116 } |
| 111 edit_model_->OnPopupDataChanged(inline_autocomplete_text, NULL, | 117 edit_model_->OnPopupDataChanged(inline_autocomplete_text, NULL, |
| 112 keyword, is_keyword_hint); | 118 keyword, is_keyword_hint); |
| 113 } else { | 119 } else { |
| 114 edit_model_->OnPopupDataChanged(match.fill_into_edit, ¤t_destination, | 120 edit_model_->OnPopupDataChanged(match.fill_into_edit, ¤t_destination, |
| 115 keyword, is_keyword_hint); | 121 keyword, is_keyword_hint); |
| 116 } | 122 } |
| 117 | 123 |
| 118 // Repaint old and new selected lines immediately, so that the edit doesn't | 124 // Repaint old and new selected lines immediately, so that the edit doesn't |
| 119 // appear to update [much] faster than the popup. | 125 // appear to update [much] faster than the popup. |
| 120 view_->PaintUpdatesNow(); | 126 view_->PaintUpdatesNow(); |
| 121 } | 127 } |
| 122 | 128 |
| 123 void AutocompletePopupModel::ResetToDefaultMatch() { | 129 void AutocompletePopupModel::ResetToDefaultMatch() { |
| 124 const AutocompleteResult& result = this->result(); | 130 const AutocompleteResult& result = this->result(); |
| 125 CHECK(!result.empty()); | 131 CHECK(!result.empty()); |
| 126 SetSelectedLine(result.default_match() - result.begin(), true, false); | 132 SetSelectedLine(result.default_match() - result.begin(), true, false); |
| 127 view_->OnDragCanceled(); | 133 view_->OnDragCanceled(); |
| 128 } | 134 } |
| 129 | 135 |
| 130 bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match, | |
| 131 string16* keyword) const { | |
| 132 // Assume we have no keyword until we find otherwise. | |
| 133 keyword->clear(); | |
| 134 | |
| 135 if (match.template_url && | |
| 136 TemplateURL::SupportsReplacement(match.template_url) && | |
| 137 match.transition == content::PAGE_TRANSITION_KEYWORD) { | |
| 138 // The current match is a keyword, return that as the selected keyword. | |
| 139 keyword->assign(match.template_url->keyword()); | |
| 140 return false; | |
| 141 } | |
| 142 | |
| 143 // See if the current match's fill_into_edit corresponds to a keyword. | |
| 144 return GetKeywordForText(match.fill_into_edit, keyword); | |
| 145 } | |
| 146 | |
| 147 bool AutocompletePopupModel::GetKeywordForText(const string16& text, | |
| 148 string16* keyword) const { | |
| 149 // Creates keyword_hint first in case |keyword| is a pointer to |text|. | |
| 150 const string16 keyword_hint(TemplateURLService::CleanUserInputKeyword(text)); | |
| 151 | |
| 152 // Assume we have no keyword until we find otherwise. | |
| 153 keyword->clear(); | |
| 154 | |
| 155 if (keyword_hint.empty()) | |
| 156 return false; | |
| 157 Profile* profile = edit_model_->profile(); | |
| 158 TemplateURLService* url_service = | |
| 159 TemplateURLServiceFactory::GetForProfile(profile); | |
| 160 if (!url_service) | |
| 161 return false; | |
| 162 url_service->Load(); | |
| 163 | |
| 164 // Don't provide a hint if this keyword doesn't support replacement. | |
| 165 const TemplateURL* const template_url = | |
| 166 url_service->GetTemplateURLForKeyword(keyword_hint); | |
| 167 if (!TemplateURL::SupportsReplacement(template_url)) | |
| 168 return false; | |
| 169 | |
| 170 // Don't provide a hint for inactive/disabled extension keywords. | |
| 171 if (template_url->IsExtensionKeyword()) { | |
| 172 const Extension* extension = profile->GetExtensionService()-> | |
| 173 GetExtensionById(template_url->GetExtensionId(), false); | |
| 174 if (!extension || (profile->IsOffTheRecord() && | |
| 175 !profile->GetExtensionService()->IsIncognitoEnabled(extension->id()))) | |
| 176 return false; | |
| 177 } | |
| 178 | |
| 179 keyword->assign(keyword_hint); | |
| 180 return true; | |
| 181 } | |
| 182 | |
| 183 void AutocompletePopupModel::Move(int count) { | 136 void AutocompletePopupModel::Move(int count) { |
| 184 const AutocompleteResult& result = this->result(); | 137 const AutocompleteResult& result = this->result(); |
| 185 if (result.empty()) | 138 if (result.empty()) |
| 186 return; | 139 return; |
| 187 | 140 |
| 188 // The user is using the keyboard to change the selection, so stop tracking | 141 // The user is using the keyboard to change the selection, so stop tracking |
| 189 // hover. | 142 // hover. |
| 190 SetHoveredLine(kNoMatch); | 143 SetHoveredLine(kNoMatch); |
| 191 | 144 |
| 192 // Clamp the new line to [0, result_.count() - 1]. | 145 // Clamp the new line to [0, result_.count() - 1]. |
| 193 const size_t new_line = selected_line_ + count; | 146 const size_t new_line = selected_line_ + count; |
| 194 SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line, | 147 SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line, |
| 195 false, false); | 148 false, false); |
| 196 } | 149 } |
| 197 | 150 |
| 151 void AutocompletePopupModel::SetSelectedLineState(LineState state) { |
| 152 DCHECK(!result().empty()); |
| 153 DCHECK_NE(kNoMatch, selected_line_); |
| 154 |
| 155 const AutocompleteMatch& match = result().match_at(selected_line_); |
| 156 DCHECK(match.associated_keyword.get()); |
| 157 |
| 158 selected_line_state_ = state; |
| 159 view_->InvalidateLine(selected_line_); |
| 160 } |
| 161 |
| 198 void AutocompletePopupModel::TryDeletingCurrentItem() { | 162 void AutocompletePopupModel::TryDeletingCurrentItem() { |
| 199 // We could use InfoForCurrentSelection() here, but it seems better to try | 163 // We could use InfoForCurrentSelection() here, but it seems better to try |
| 200 // and shift-delete the actual selection, rather than any "in progress, not | 164 // and shift-delete the actual selection, rather than any "in progress, not |
| 201 // yet visible" one. | 165 // yet visible" one. |
| 202 if (selected_line_ == kNoMatch) | 166 if (selected_line_ == kNoMatch) |
| 203 return; | 167 return; |
| 204 | 168 |
| 205 // Cancel the query so the matches don't change on the user. | 169 // Cancel the query so the matches don't change on the user. |
| 206 autocomplete_controller()->Stop(false); | 170 autocomplete_controller()->Stop(false); |
| 207 | 171 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 236 match.template_url->GetExtensionId()); | 200 match.template_url->GetExtensionId()); |
| 237 } | 201 } |
| 238 | 202 |
| 239 void AutocompletePopupModel::OnResultChanged() { | 203 void AutocompletePopupModel::OnResultChanged() { |
| 240 const AutocompleteResult& result = this->result(); | 204 const AutocompleteResult& result = this->result(); |
| 241 selected_line_ = result.default_match() == result.end() ? | 205 selected_line_ = result.default_match() == result.end() ? |
| 242 kNoMatch : static_cast<size_t>(result.default_match() - result.begin()); | 206 kNoMatch : static_cast<size_t>(result.default_match() - result.begin()); |
| 243 // There had better not be a nonempty result set with no default match. | 207 // There had better not be a nonempty result set with no default match. |
| 244 CHECK((selected_line_ != kNoMatch) || result.empty()); | 208 CHECK((selected_line_ != kNoMatch) || result.empty()); |
| 245 manually_selected_match_.Clear(); | 209 manually_selected_match_.Clear(); |
| 210 selected_line_state_ = NORMAL; |
| 246 // If we're going to trim the window size to no longer include the hovered | 211 // If we're going to trim the window size to no longer include the hovered |
| 247 // line, turn hover off. Practically, this shouldn't happen, but it | 212 // line, turn hover off. Practically, this shouldn't happen, but it |
| 248 // doesn't hurt to be defensive. | 213 // doesn't hurt to be defensive. |
| 249 if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_)) | 214 if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_)) |
| 250 SetHoveredLine(kNoMatch); | 215 SetHoveredLine(kNoMatch); |
| 251 | 216 |
| 252 view_->UpdatePopupAppearance(); | 217 view_->UpdatePopupAppearance(); |
| 253 } | 218 } |
| OLD | NEW |