Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / ui / app_list / search / mixer.cc
blob3466d501119fd8a70a676fc67f66c989b762057c
1 // Copyright 2013 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 "chrome/browser/ui/app_list/search/mixer.h"
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <vector>
12 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
13 #include "chrome/browser/ui/app_list/search/search_provider.h"
15 namespace app_list {
17 namespace {
19 // Maximum number of results to show.
20 const size_t kMaxResults = 6;
21 const size_t kMaxMainGroupResults = 4;
22 const size_t kMaxWebstoreResults = 2;
24 // A value to indicate no max number of results limit.
25 const size_t kNoMaxResultsLimit = 0;
27 // Used for sorting and mixing results.
28 struct SortData {
29 SortData()
30 : result(NULL),
31 score(0.0) {
33 SortData(ChromeSearchResult* result, double score)
34 : result(result),
35 score(score) {
38 bool operator<(const SortData& other) const {
39 // This data precedes (less than) |other| if it has higher score.
40 return score > other.score;
43 ChromeSearchResult* result; // Not owned.
44 double score;
46 typedef std::vector<SortData> SortedResults;
48 // Removes duplicates from |results|.
49 void RemoveDuplicates(SortedResults* results) {
50 SortedResults final;
51 final.reserve(results->size());
53 std::set<std::string> id_set;
54 for (SortedResults::iterator it = results->begin();
55 it != results->end();
56 ++it) {
57 const std::string& id = it->result->id();
58 if (id_set.find(id) != id_set.end())
59 continue;
61 id_set.insert(id);
62 final.push_back(*it);
65 results->swap(final);
68 // Publishes the given |results| to |ui_results|. Reuse existing ones to avoid
69 // flickering.
70 void Publish(const SortedResults& results,
71 AppListModel::SearchResults* ui_results) {
72 for (size_t i = 0; i < results.size(); ++i) {
73 ChromeSearchResult* result = results[i].result;
75 ChromeSearchResult* ui_result = i < ui_results->item_count() ?
76 static_cast<ChromeSearchResult*>(ui_results->GetItemAt(i)) : NULL;
77 if (ui_result && ui_result->id() == result->id()) {
78 ui_result->set_title(result->title());
79 ui_result->set_title_tags(result->title_tags());
80 ui_result->set_details(result->details());
81 ui_result->set_details_tags(result->details_tags());
82 ui_results->NotifyItemsChanged(i, 1);
83 } else {
84 if (ui_result)
85 ui_results->DeleteAt(i);
86 ui_results->AddAt(i, result->Duplicate().release());
90 while (ui_results->item_count() > results.size())
91 ui_results->DeleteAt(ui_results->item_count() - 1);
94 } // namespace
96 // Used to group relevant providers together fox mixing their results.
97 class Mixer::Group {
98 public:
99 Group(size_t max_results, double boost)
100 : max_results_(max_results),
101 boost_(boost) {
103 ~Group() {}
105 void AddProvider(SearchProvider* provider) {
106 providers_.push_back(provider);
109 void FetchResults(const KnownResults& known_results) {
110 results_.clear();
112 for (Providers::const_iterator provider_it = providers_.begin();
113 provider_it != providers_.end();
114 ++provider_it) {
115 for (SearchProvider::Results::const_iterator
116 result_it = (*provider_it)->results().begin();
117 result_it != (*provider_it)->results().end();
118 ++result_it) {
119 DCHECK_GE((*result_it)->relevance(), 0.0);
120 DCHECK_LE((*result_it)->relevance(), 1.0);
121 DCHECK(!(*result_it)->id().empty());
123 double boost = boost_;
124 KnownResults::const_iterator known_it =
125 known_results.find((*result_it)->id());
126 if (known_it != known_results.end()) {
127 switch (known_it->second) {
128 case PERFECT_PRIMARY:
129 boost = 4.0;
130 break;
131 case PREFIX_PRIMARY:
132 boost = 3.75;
133 break;
134 case PERFECT_SECONDARY:
135 boost = 3.25;
136 break;
137 case PREFIX_SECONDARY:
138 boost = 3.0;
139 break;
140 case UNKNOWN_RESULT:
141 NOTREACHED() << "Unknown result in KnownResults?";
142 break;
146 results_.push_back(
147 SortData(*result_it, (*result_it)->relevance() + boost));
151 std::sort(results_.begin(), results_.end());
152 if (max_results_ != kNoMaxResultsLimit && results_.size() > max_results_)
153 results_.resize(max_results_);
156 const SortedResults& results() const { return results_; }
158 private:
159 typedef std::vector<SearchProvider*> Providers;
160 const size_t max_results_;
161 const double boost_;
163 Providers providers_; // Not owned.
164 SortedResults results_;
166 DISALLOW_COPY_AND_ASSIGN(Group);
169 Mixer::Mixer(AppListModel::SearchResults* ui_results)
170 : ui_results_(ui_results) {}
171 Mixer::~Mixer() {}
173 void Mixer::Init() {
174 groups_.push_back(new Group(kMaxMainGroupResults, 2.0));
175 groups_.push_back(new Group(kNoMaxResultsLimit, 1.0));
176 groups_.push_back(new Group(kMaxWebstoreResults, 0.0));
179 void Mixer::AddProviderToGroup(GroupId group, SearchProvider* provider) {
180 size_t group_index = static_cast<size_t>(group);
181 groups_[group_index]->AddProvider(provider);
184 void Mixer::MixAndPublish(const KnownResults& known_results) {
185 FetchResults(known_results);
187 SortedResults results;
188 results.reserve(kMaxResults);
190 // Adds main group and web store results first.
191 results.insert(results.end(),
192 groups_[MAIN_GROUP]->results().begin(),
193 groups_[MAIN_GROUP]->results().end());
194 results.insert(results.end(),
195 groups_[WEBSTORE_GROUP]->results().begin(),
196 groups_[WEBSTORE_GROUP]->results().end());
198 // Collapse duplicate apps from local and web store.
199 RemoveDuplicates(&results);
201 DCHECK_GE(kMaxResults, results.size());
202 size_t remaining_slots = kMaxResults - results.size();
204 // Reserves at least one slot for the omnibox result. If there is no available
205 // slot for omnibox results, removes the last one from web store.
206 const size_t omnibox_results = groups_[OMNIBOX_GROUP]->results().size();
207 if (!remaining_slots && omnibox_results)
208 results.pop_back();
210 remaining_slots = std::min(kMaxResults - results.size(), omnibox_results);
211 results.insert(results.end(),
212 groups_[OMNIBOX_GROUP]->results().begin(),
213 groups_[OMNIBOX_GROUP]->results().begin() + remaining_slots);
215 std::sort(results.begin(), results.end());
216 RemoveDuplicates(&results);
217 if (results.size() > kMaxResults)
218 results.resize(kMaxResults);
220 Publish(results, ui_results_);
223 void Mixer::FetchResults(const KnownResults& known_results) {
224 for (Groups::iterator group_it = groups_.begin();
225 group_it != groups_.end();
226 ++group_it) {
227 (*group_it)->FetchResults(known_results);
231 } // namespace app_list