Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / components / omnibox / browser / omnibox_popup_model.cc
blob0ec1e08a246d80ade1cc0ed7bb09490f66e075c5
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/omnibox/browser/omnibox_popup_model.h"
7 #include <algorithm>
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/bookmarks/browser/bookmark_model.h"
12 #include "components/omnibox/browser/autocomplete_match.h"
13 #include "components/omnibox/browser/omnibox_client.h"
14 #include "components/omnibox/browser/omnibox_popup_model_observer.h"
15 #include "components/omnibox/browser/omnibox_popup_view.h"
16 #include "components/search_engines/template_url.h"
17 #include "components/search_engines/template_url_service.h"
18 #include "third_party/icu/source/common/unicode/ubidi.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/image/image.h"
22 using bookmarks::BookmarkModel;
24 ///////////////////////////////////////////////////////////////////////////////
25 // OmniboxPopupModel
27 const size_t OmniboxPopupModel::kNoMatch = static_cast<size_t>(-1);
29 OmniboxPopupModel::OmniboxPopupModel(
30 OmniboxPopupView* popup_view,
31 OmniboxEditModel* edit_model)
32 : view_(popup_view),
33 edit_model_(edit_model),
34 hovered_line_(kNoMatch),
35 selected_line_(kNoMatch),
36 selected_line_state_(NORMAL) {
37 edit_model->set_popup_model(this);
40 OmniboxPopupModel::~OmniboxPopupModel() {
43 // static
44 void OmniboxPopupModel::ComputeMatchMaxWidths(int contents_width,
45 int separator_width,
46 int description_width,
47 int available_width,
48 bool allow_shrinking_contents,
49 int* contents_max_width,
50 int* description_max_width) {
51 available_width = std::max(available_width, 0);
52 *contents_max_width = std::min(contents_width, available_width);
53 *description_max_width = description_width;
55 // If the description is empty, the contents can get the full width.
56 if (!description_width)
57 return;
59 // If we want to display the description, we need to reserve enough space for
60 // the separator.
61 available_width -= separator_width;
62 if (available_width < 0) {
63 *description_max_width = 0;
64 return;
67 if (contents_width + description_width > available_width) {
68 if (allow_shrinking_contents) {
69 // Try to split the available space fairly between contents and
70 // description (if one wants less than half, give it all it wants and
71 // give the other the remaining space; otherwise, give each half).
72 // However, if this makes the contents too narrow to show a significant
73 // amount of information, give the contents more space.
74 *contents_max_width = std::max(
75 (available_width + 1) / 2, available_width - description_width);
77 const int kMinimumContentsWidth = 300;
78 *contents_max_width = std::min(std::min(
79 std::max(*contents_max_width, kMinimumContentsWidth), contents_width),
80 available_width);
83 // Give the description the remaining space, unless this makes it too small
84 // to display anything meaningful, in which case just hide the description
85 // and let the contents take up the whole width.
86 *description_max_width =
87 std::min(description_width, available_width - *contents_max_width);
88 const int kMinimumDescriptionWidth = 75;
89 if (*description_max_width <
90 std::min(description_width, kMinimumDescriptionWidth)) {
91 *description_max_width = 0;
92 // Since we're not going to display the description, the contents can have
93 // the space we reserved for the separator.
94 available_width += separator_width;
95 *contents_max_width = std::min(contents_width, available_width);
100 bool OmniboxPopupModel::IsOpen() const {
101 return view_->IsOpen();
104 void OmniboxPopupModel::SetHoveredLine(size_t line) {
105 const bool is_disabling = (line == kNoMatch);
106 DCHECK(is_disabling || (line < result().size()));
108 if (line == hovered_line_)
109 return; // Nothing to do
111 // Make sure the old hovered line is redrawn. No need to redraw the selected
112 // line since selection overrides hover so the appearance won't change.
113 if ((hovered_line_ != kNoMatch) && (hovered_line_ != selected_line_))
114 view_->InvalidateLine(hovered_line_);
116 // Change the hover to the new line.
117 hovered_line_ = line;
118 if (!is_disabling && (hovered_line_ != selected_line_))
119 view_->InvalidateLine(hovered_line_);
122 void OmniboxPopupModel::SetSelectedLine(size_t line,
123 bool reset_to_default,
124 bool force) {
125 const AutocompleteResult& result = this->result();
126 if (result.empty())
127 return;
129 // Cancel the query so the matches don't change on the user.
130 autocomplete_controller()->Stop(false);
132 line = std::min(line, result.size() - 1);
133 const AutocompleteMatch& match = result.match_at(line);
134 if (reset_to_default) {
135 manually_selected_match_.Clear();
136 } else {
137 // Track the user's selection until they cancel it.
138 manually_selected_match_.destination_url = match.destination_url;
139 manually_selected_match_.provider_affinity = match.provider;
140 manually_selected_match_.is_history_what_you_typed_match =
141 match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED;
144 if (line == selected_line_ && !force)
145 return; // Nothing else to do.
147 // We need to update |selected_line_state_| and |selected_line_| before
148 // calling InvalidateLine(), since it will check them to determine how to
149 // draw. We also need to update |selected_line_| before calling
150 // OnPopupDataChanged(), so that when the edit notifies its controller that
151 // something has changed, the controller can get the correct updated data.
153 // NOTE: We should never reach here with no selected line; the same code that
154 // opened the popup and made it possible to get here should have also set a
155 // selected line.
156 CHECK(selected_line_ != kNoMatch);
157 GURL current_destination(result.match_at(selected_line_).destination_url);
158 const size_t prev_selected_line = selected_line_;
159 selected_line_state_ = NORMAL;
160 selected_line_ = line;
161 view_->InvalidateLine(prev_selected_line);
162 view_->InvalidateLine(selected_line_);
164 // Update the edit with the new data for this match.
165 // TODO(pkasting): If |selected_line_| moves to the controller, this can be
166 // eliminated and just become a call to the observer on the edit.
167 base::string16 keyword;
168 bool is_keyword_hint;
169 TemplateURLService* service = edit_model_->client()->GetTemplateURLService();
170 match.GetKeywordUIState(service, &keyword, &is_keyword_hint);
172 if (reset_to_default) {
173 edit_model_->OnPopupDataChanged(match.inline_autocompletion, NULL,
174 keyword, is_keyword_hint);
175 } else {
176 edit_model_->OnPopupDataChanged(match.fill_into_edit, &current_destination,
177 keyword, is_keyword_hint);
180 // Repaint old and new selected lines immediately, so that the edit doesn't
181 // appear to update [much] faster than the popup.
182 view_->PaintUpdatesNow();
185 void OmniboxPopupModel::ResetToDefaultMatch() {
186 const AutocompleteResult& result = this->result();
187 CHECK(!result.empty());
188 SetSelectedLine(result.default_match() - result.begin(), true, false);
189 view_->OnDragCanceled();
192 void OmniboxPopupModel::Move(int count) {
193 const AutocompleteResult& result = this->result();
194 if (result.empty())
195 return;
197 // The user is using the keyboard to change the selection, so stop tracking
198 // hover.
199 SetHoveredLine(kNoMatch);
201 // Clamp the new line to [0, result_.count() - 1].
202 const size_t new_line = selected_line_ + count;
203 SetSelectedLine(((count < 0) && (new_line >= selected_line_)) ? 0 : new_line,
204 false, false);
207 void OmniboxPopupModel::SetSelectedLineState(LineState state) {
208 DCHECK(!result().empty());
209 DCHECK_NE(kNoMatch, selected_line_);
211 const AutocompleteMatch& match = result().match_at(selected_line_);
212 DCHECK(match.associated_keyword.get());
214 selected_line_state_ = state;
215 view_->InvalidateLine(selected_line_);
218 void OmniboxPopupModel::TryDeletingCurrentItem() {
219 // We could use GetInfoForCurrentText() here, but it seems better to try
220 // and shift-delete the actual selection, rather than any "in progress, not
221 // yet visible" one.
222 if (selected_line_ == kNoMatch)
223 return;
225 // Cancel the query so the matches don't change on the user.
226 autocomplete_controller()->Stop(false);
228 const AutocompleteMatch& match = result().match_at(selected_line_);
229 if (match.SupportsDeletion()) {
230 const size_t selected_line = selected_line_;
231 const bool was_temporary_text = !manually_selected_match_.empty();
233 // This will synchronously notify both the edit and us that the results
234 // have changed, causing both to revert to the default match.
235 autocomplete_controller()->DeleteMatch(match);
236 const AutocompleteResult& result = this->result();
237 if (!result.empty() &&
238 (was_temporary_text || selected_line != selected_line_)) {
239 // Move the selection to the next choice after the deleted one.
240 // SetSelectedLine() will clamp to take care of the case where we deleted
241 // the last item.
242 // TODO(pkasting): Eventually the controller should take care of this
243 // before notifying us, reducing flicker. At that point the check for
244 // deletability can move there too.
245 SetSelectedLine(selected_line, false, true);
250 gfx::Image OmniboxPopupModel::GetIconIfExtensionMatch(
251 const AutocompleteMatch& match) const {
252 return edit_model_->client()->GetIconIfExtensionMatch(match);
255 bool OmniboxPopupModel::IsStarredMatch(const AutocompleteMatch& match) const {
256 BookmarkModel* bookmark_model = edit_model_->client()->GetBookmarkModel();
257 return bookmark_model && bookmark_model->IsBookmarked(match.destination_url);
260 void OmniboxPopupModel::OnResultChanged() {
261 answer_bitmap_ = SkBitmap();
262 const AutocompleteResult& result = this->result();
263 selected_line_ = result.default_match() == result.end() ?
264 kNoMatch : static_cast<size_t>(result.default_match() - result.begin());
265 // There had better not be a nonempty result set with no default match.
266 CHECK((selected_line_ != kNoMatch) || result.empty());
267 manually_selected_match_.Clear();
268 selected_line_state_ = NORMAL;
269 // If we're going to trim the window size to no longer include the hovered
270 // line, turn hover off. Practically, this shouldn't happen, but it
271 // doesn't hurt to be defensive.
272 if ((hovered_line_ != kNoMatch) && (result.size() <= hovered_line_))
273 SetHoveredLine(kNoMatch);
275 bool popup_was_open = view_->IsOpen();
276 view_->UpdatePopupAppearance();
277 // If popup has just been shown or hidden, notify observers.
278 if (view_->IsOpen() != popup_was_open) {
279 FOR_EACH_OBSERVER(OmniboxPopupModelObserver, observers_,
280 OnOmniboxPopupShownOrHidden());
284 void OmniboxPopupModel::AddObserver(OmniboxPopupModelObserver* observer) {
285 observers_.AddObserver(observer);
288 void OmniboxPopupModel::RemoveObserver(OmniboxPopupModelObserver* observer) {
289 observers_.RemoveObserver(observer);
292 void OmniboxPopupModel::SetAnswerBitmap(const SkBitmap& bitmap) {
293 answer_bitmap_ = bitmap;
294 view_->UpdatePopupAppearance();