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_| before calling OnPopupDataChanged(), so |
Peter Kasting
2012/01/12 22:59:59
Nit: "We need to update |selected_line_state_| and
| |
87 // that when the edit notifies its controller that something has changed, the | 88 // that when the edit notifies its controller that something has changed, the |
88 // controller can get the correct updated data. | 89 // controller can get the correct updated data. |
89 // | 90 // |
90 // NOTE: We should never reach here with no selected line; the same code that | 91 // 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 | 92 // opened the popup and made it possible to get here should have also set a |
92 // selected line. | 93 // selected line. |
93 CHECK(selected_line_ != kNoMatch); | 94 CHECK(selected_line_ != kNoMatch); |
94 GURL current_destination(result.match_at(selected_line_).destination_url); | 95 GURL current_destination(result.match_at(selected_line_).destination_url); |
95 view_->InvalidateLine(selected_line_); | 96 const size_t prev_selected_line = selected_line_; |
97 selected_line_state_ = NORMAL; | |
96 selected_line_ = line; | 98 selected_line_ = line; |
99 view_->InvalidateLine(prev_selected_line); | |
97 view_->InvalidateLine(selected_line_); | 100 view_->InvalidateLine(selected_line_); |
98 | 101 |
99 // Update the edit with the new data for this match. | 102 // Update the edit with the new data for this match. |
100 // TODO(pkasting): If |selected_line_| moves to the controller, this can be | 103 // 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. | 104 // eliminated and just become a call to the observer on the edit. |
102 string16 keyword; | 105 string16 keyword; |
103 const bool is_keyword_hint = GetKeywordForMatch(match, &keyword); | 106 const bool is_keyword_hint = match.GetKeyword(&keyword); |
107 | |
104 if (reset_to_default) { | 108 if (reset_to_default) { |
105 string16 inline_autocomplete_text; | 109 string16 inline_autocomplete_text; |
106 if ((match.inline_autocomplete_offset != string16::npos) && | 110 if ((match.inline_autocomplete_offset != string16::npos) && |
107 (match.inline_autocomplete_offset < match.fill_into_edit.length())) { | 111 (match.inline_autocomplete_offset < match.fill_into_edit.length())) { |
108 inline_autocomplete_text = | 112 inline_autocomplete_text = |
109 match.fill_into_edit.substr(match.inline_autocomplete_offset); | 113 match.fill_into_edit.substr(match.inline_autocomplete_offset); |
110 } | 114 } |
111 edit_model_->OnPopupDataChanged(inline_autocomplete_text, NULL, | 115 edit_model_->OnPopupDataChanged(inline_autocomplete_text, NULL, |
112 keyword, is_keyword_hint); | 116 keyword, is_keyword_hint); |
113 } else { | 117 } else { |
114 edit_model_->OnPopupDataChanged(match.fill_into_edit, ¤t_destination, | 118 edit_model_->OnPopupDataChanged(match.fill_into_edit, ¤t_destination, |
115 keyword, is_keyword_hint); | 119 keyword, is_keyword_hint); |
116 } | 120 } |
117 | 121 |
118 // Repaint old and new selected lines immediately, so that the edit doesn't | 122 // Repaint old and new selected lines immediately, so that the edit doesn't |
119 // appear to update [much] faster than the popup. | 123 // appear to update [much] faster than the popup. |
120 view_->PaintUpdatesNow(); | 124 view_->PaintUpdatesNow(); |
121 } | 125 } |
122 | 126 |
123 void AutocompletePopupModel::ResetToDefaultMatch() { | 127 void AutocompletePopupModel::ResetToDefaultMatch() { |
124 const AutocompleteResult& result = this->result(); | 128 const AutocompleteResult& result = this->result(); |
125 CHECK(!result.empty()); | 129 CHECK(!result.empty()); |
126 SetSelectedLine(result.default_match() - result.begin(), true, false); | 130 SetSelectedLine(result.default_match() - result.begin(), true, false); |
127 view_->OnDragCanceled(); | 131 view_->OnDragCanceled(); |
128 } | 132 } |
129 | 133 |
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) { | 134 void AutocompletePopupModel::Move(int count) { |
184 const AutocompleteResult& result = this->result(); | 135 const AutocompleteResult& result = this->result(); |
185 if (result.empty()) | 136 if (result.empty()) |
186 return; | 137 return; |
187 | 138 |
188 // The user is using the keyboard to change the selection, so stop tracking | 139 // The user is using the keyboard to change the selection, so stop tracking |
189 // hover. | 140 // hover. |
190 SetHoveredLine(kNoMatch); | 141 SetHoveredLine(kNoMatch); |
191 | 142 |
192 // Clamp the new line to [0, result_.count() - 1]. | 143 // Clamp the new line to [0, result_.count() - 1]. |
193 const size_t new_line = selected_line_ + count; | 144 const size_t new_line = selected_line_ + count; |
194 SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line, | 145 SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line, |
195 false, false); | 146 false, false); |
196 } | 147 } |
197 | 148 |
149 void AutocompletePopupModel::SetSelectedLineState(LineState state) { | |
150 DCHECK(!result().empty()); | |
151 DCHECK_NE(kNoMatch, selected_line_); | |
152 | |
153 const AutocompleteMatch& match = result().match_at(selected_line_); | |
154 DCHECK(match.associated_keyword.get()); | |
155 | |
156 selected_line_state_ = state; | |
157 view_->InvalidateLine(selected_line_); | |
158 } | |
159 | |
198 void AutocompletePopupModel::TryDeletingCurrentItem() { | 160 void AutocompletePopupModel::TryDeletingCurrentItem() { |
199 // We could use InfoForCurrentSelection() here, but it seems better to try | 161 // We could use InfoForCurrentSelection() here, but it seems better to try |
200 // and shift-delete the actual selection, rather than any "in progress, not | 162 // and shift-delete the actual selection, rather than any "in progress, not |
201 // yet visible" one. | 163 // yet visible" one. |
202 if (selected_line_ == kNoMatch) | 164 if (selected_line_ == kNoMatch) |
203 return; | 165 return; |
204 | 166 |
205 // Cancel the query so the matches don't change on the user. | 167 // Cancel the query so the matches don't change on the user. |
206 autocomplete_controller()->Stop(false); | 168 autocomplete_controller()->Stop(false); |
207 | 169 |
(...skipping 28 matching lines...) Expand all Loading... | |
236 match.template_url->GetExtensionId()); | 198 match.template_url->GetExtensionId()); |
237 } | 199 } |
238 | 200 |
239 void AutocompletePopupModel::OnResultChanged() { | 201 void AutocompletePopupModel::OnResultChanged() { |
240 const AutocompleteResult& result = this->result(); | 202 const AutocompleteResult& result = this->result(); |
241 selected_line_ = result.default_match() == result.end() ? | 203 selected_line_ = result.default_match() == result.end() ? |
242 kNoMatch : static_cast<size_t>(result.default_match() - result.begin()); | 204 kNoMatch : static_cast<size_t>(result.default_match() - result.begin()); |
243 // There had better not be a nonempty result set with no default match. | 205 // There had better not be a nonempty result set with no default match. |
244 CHECK((selected_line_ != kNoMatch) || result.empty()); | 206 CHECK((selected_line_ != kNoMatch) || result.empty()); |
245 manually_selected_match_.Clear(); | 207 manually_selected_match_.Clear(); |
208 selected_line_state_ = NORMAL; | |
246 // If we're going to trim the window size to no longer include the hovered | 209 // 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 | 210 // line, turn hover off. Practically, this shouldn't happen, but it |
248 // doesn't hurt to be defensive. | 211 // doesn't hurt to be defensive. |
249 if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_)) | 212 if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_)) |
250 SetHoveredLine(kNoMatch); | 213 SetHoveredLine(kNoMatch); |
251 | 214 |
252 view_->UpdatePopupAppearance(); | 215 view_->UpdatePopupAppearance(); |
253 } | 216 } |
OLD | NEW |