OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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/ui/app_list/search/mixer.h" | 5 #include "chrome/browser/ui/app_list/search/mixer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> |
8 #include <set> | 9 #include <set> |
9 #include <string> | 10 #include <string> |
10 #include <vector> | 11 #include <vector> |
11 | 12 |
12 #include "chrome/browser/ui/app_list/search/chrome_search_result.h" | 13 #include "chrome/browser/ui/app_list/search/chrome_search_result.h" |
13 #include "ui/app_list/search_provider.h" | 14 #include "ui/app_list/search_provider.h" |
14 | 15 |
15 namespace app_list { | 16 namespace app_list { |
16 | 17 |
17 namespace { | 18 namespace { |
18 | 19 |
19 // Maximum number of results to show. | 20 // Maximum number of results to show. |
20 const size_t kMaxResults = 6; | 21 const size_t kMaxResults = 6; |
21 const size_t kMaxMainGroupResults = 4; | 22 const size_t kMaxMainGroupResults = 4; |
22 const size_t kMaxWebstoreResults = 2; | 23 const size_t kMaxWebstoreResults = 2; |
23 const size_t kMaxPeopleResults = 2; | 24 const size_t kMaxPeopleResults = 2; |
24 | 25 |
25 // A value to indicate no max number of results limit. | 26 // A value to indicate no max number of results limit. |
26 const size_t kNoMaxResultsLimit = 0; | 27 const size_t kNoMaxResultsLimit = 0; |
27 | 28 |
| 29 void UpdateResult(const ChromeSearchResult& source, |
| 30 ChromeSearchResult* target) { |
| 31 target->set_title(source.title()); |
| 32 target->set_title_tags(source.title_tags()); |
| 33 target->set_details(source.details()); |
| 34 target->set_details_tags(source.details_tags()); |
| 35 } |
| 36 |
28 } // namespace | 37 } // namespace |
29 | 38 |
30 Mixer::SortData::SortData() : result(NULL), score(0.0) { | 39 Mixer::SortData::SortData() : result(NULL), score(0.0) { |
31 } | 40 } |
32 | 41 |
33 Mixer::SortData::SortData(ChromeSearchResult* result, double score) | 42 Mixer::SortData::SortData(ChromeSearchResult* result, double score) |
34 : result(result), score(score) { | 43 : result(result), score(score) { |
35 } | 44 } |
36 | 45 |
37 bool Mixer::SortData::operator<(const SortData& other) const { | 46 bool Mixer::SortData::operator<(const SortData& other) const { |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 groups_[OMNIBOX_GROUP]->results().begin() + remaining_slots); | 173 groups_[OMNIBOX_GROUP]->results().begin() + remaining_slots); |
165 | 174 |
166 std::sort(results.begin(), results.end()); | 175 std::sort(results.begin(), results.end()); |
167 RemoveDuplicates(&results); | 176 RemoveDuplicates(&results); |
168 if (results.size() > kMaxResults) | 177 if (results.size() > kMaxResults) |
169 results.resize(kMaxResults); | 178 results.resize(kMaxResults); |
170 | 179 |
171 Publish(results, ui_results_); | 180 Publish(results, ui_results_); |
172 } | 181 } |
173 | 182 |
174 void Mixer::Publish(const SortedResults& results, | 183 void Mixer::Publish(const SortedResults& new_results, |
175 AppListModel::SearchResults* ui_results) { | 184 AppListModel::SearchResults* ui_results) { |
176 for (size_t i = 0; i < results.size(); ++i) { | 185 typedef std::map<std::string, ChromeSearchResult*> IdToResultMap; |
177 ChromeSearchResult* result = results[i].result; | |
178 | 186 |
| 187 // The following algorithm is used: |
| 188 // 1. Transform the |ui_results| list into an unordered map from result ID |
| 189 // to item. |
| 190 // 2. Use the order of items in |new_results| to build an ordered list. If the |
| 191 // result IDs exist in the map, update and use the item in the map and delete |
| 192 // it from the map afterwards. Otherwise, clone new items from |new_results|. |
| 193 // 3. Delete the objects remaining in the map as they are unused. |
| 194 |
| 195 // A map of the items in |ui_results| that takes ownership of the items. |
| 196 IdToResultMap ui_results_map; |
| 197 for (size_t i = 0; i < ui_results->item_count(); ++i) { |
179 ChromeSearchResult* ui_result = | 198 ChromeSearchResult* ui_result = |
180 i < ui_results->item_count() | 199 static_cast<ChromeSearchResult*>(ui_results->GetItemAt(i)); |
181 ? static_cast<ChromeSearchResult*>(ui_results->GetItemAt(i)) | 200 ui_results_map[ui_result->id()] = ui_result; |
182 : NULL; | 201 } |
183 if (ui_result && ui_result->id() == result->id()) { | 202 // We have to erase all results at once so that observers are notified with |
184 ui_result->set_title(result->title()); | 203 // meaningful indexes. |
185 ui_result->set_title_tags(result->title_tags()); | 204 ui_results->RemoveAll(); |
186 ui_result->set_details(result->details()); | 205 |
187 ui_result->set_details_tags(result->details_tags()); | 206 // Add items back to |ui_results| in the order of |new_results|. |
188 ui_results->NotifyItemsChanged(i, 1); | 207 for (size_t i = 0; i < new_results.size(); ++i) { |
| 208 ChromeSearchResult* new_result = new_results[i].result; |
| 209 IdToResultMap::const_iterator ui_result_it = |
| 210 ui_results_map.find(new_result->id()); |
| 211 if (ui_result_it != ui_results_map.end()) { |
| 212 // Update and use the old result if it exists. |
| 213 ChromeSearchResult* ui_result = ui_result_it->second; |
| 214 UpdateResult(*new_result, ui_result); |
| 215 |
| 216 // |ui_results| takes back ownership from |ui_results_map| here. |
| 217 ui_results->Add(ui_result); |
| 218 |
| 219 // Remove the item from the map so that it ends up only with unused |
| 220 // results. |
| 221 ui_results_map.erase(ui_result->id()); |
189 } else { | 222 } else { |
190 if (ui_result) | 223 // Copy the result from |new_results| otherwise. |
191 ui_results->DeleteAt(i); | 224 ui_results->Add(new_result->Duplicate().release()); |
192 ui_results->AddAt(i, result->Duplicate().release()); | |
193 } | 225 } |
194 } | 226 } |
195 | 227 |
196 while (ui_results->item_count() > results.size()) | 228 // Delete the results remaining in the map as they are not in the new results. |
197 ui_results->DeleteAt(ui_results->item_count() - 1); | 229 for (IdToResultMap::const_iterator ui_result_it = ui_results_map.begin(); |
| 230 ui_result_it != ui_results_map.end(); |
| 231 ++ui_result_it) { |
| 232 delete ui_result_it->second; |
| 233 } |
198 } | 234 } |
199 | 235 |
200 void Mixer::RemoveDuplicates(SortedResults* results) { | 236 void Mixer::RemoveDuplicates(SortedResults* results) { |
201 SortedResults final; | 237 SortedResults final; |
202 final.reserve(results->size()); | 238 final.reserve(results->size()); |
203 | 239 |
204 std::set<std::string> id_set; | 240 std::set<std::string> id_set; |
205 for (SortedResults::iterator it = results->begin(); it != results->end(); | 241 for (SortedResults::iterator it = results->begin(); it != results->end(); |
206 ++it) { | 242 ++it) { |
207 const std::string& id = it->result->id(); | 243 const std::string& id = it->result->id(); |
208 if (id_set.find(id) != id_set.end()) | 244 if (id_set.find(id) != id_set.end()) |
209 continue; | 245 continue; |
210 | 246 |
211 id_set.insert(id); | 247 id_set.insert(id); |
212 final.push_back(*it); | 248 final.push_back(*it); |
213 } | 249 } |
214 | 250 |
215 results->swap(final); | 251 results->swap(final); |
216 } | 252 } |
217 | 253 |
218 void Mixer::FetchResults(const KnownResults& known_results) { | 254 void Mixer::FetchResults(const KnownResults& known_results) { |
219 for (Groups::iterator group_it = groups_.begin(); | 255 for (Groups::iterator group_it = groups_.begin(); |
220 group_it != groups_.end(); | 256 group_it != groups_.end(); |
221 ++group_it) { | 257 ++group_it) { |
222 (*group_it)->FetchResults(known_results); | 258 (*group_it)->FetchResults(known_results); |
223 } | 259 } |
224 } | 260 } |
225 | 261 |
226 } // namespace app_list | 262 } // namespace app_list |
OLD | NEW |