OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "ui/app_list/app_list_model.h" | 5 #include "ui/app_list/app_list_model.h" |
6 | 6 |
7 #include "ui/app_list/app_list_constants.h" | |
7 #include "ui/app_list/app_list_item_model.h" | 8 #include "ui/app_list/app_list_item_model.h" |
8 #include "ui/app_list/app_list_model_observer.h" | 9 #include "ui/app_list/app_list_model_observer.h" |
9 #include "ui/app_list/search_box_model.h" | 10 #include "ui/app_list/search_box_model.h" |
10 #include "ui/app_list/search_result.h" | 11 #include "ui/app_list/search_result.h" |
12 #include "ui/base/models/list_model_observer.h" | |
11 | 13 |
12 namespace app_list { | 14 namespace app_list { |
13 | 15 |
14 AppListModel::User::User() : active(false) {} | 16 AppListModel::User::User() : active(false) {} |
15 | 17 |
16 AppListModel::User::~User() {} | 18 AppListModel::User::~User() {} |
17 | 19 |
20 class AppListModel::ItemPage : public ui::ListModelObserver { | |
21 public: | |
22 ItemPage(AppListModel* model, size_t page_idx) | |
23 : model_(model), | |
24 page_idx_(page_idx) { | |
25 app_items_.AddObserver(this); | |
26 } | |
27 virtual ~ItemPage() { | |
28 app_items_.RemoveObserver(this); | |
29 } | |
30 virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE { | |
31 FOR_EACH_OBSERVER(AppListModelObserver, | |
32 model_->observers_, | |
33 OnListItemsAdded(page_idx_, start, count)); | |
34 } | |
35 virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE { | |
36 FOR_EACH_OBSERVER(AppListModelObserver, | |
37 model_->observers_, | |
38 OnListItemsRemoved(page_idx_, start, count)); | |
39 } | |
40 virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE { | |
41 AppListItemModel* item = app_items_.GetItemAt(target_index); | |
42 model_->SetItemPosition(item, page_idx_, target_index); | |
43 FOR_EACH_OBSERVER(AppListModelObserver, | |
44 model_->observers_, | |
45 OnListItemMoved(page_idx_, index, target_index)); | |
46 } | |
47 virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE { | |
48 NOTREACHED(); | |
49 } | |
50 | |
51 AppItems* app_items() { return &app_items_; } | |
52 | |
53 private: | |
54 AppListModel* model_; | |
55 size_t page_idx_; | |
56 AppItems app_items_; | |
57 | |
58 DISALLOW_COPY_AND_ASSIGN(ItemPage); | |
59 }; | |
60 | |
18 AppListModel::AppListModel() | 61 AppListModel::AppListModel() |
19 : apps_(new Apps), | 62 : search_box_(new SearchBoxModel), |
20 search_box_(new SearchBoxModel), | |
21 results_(new SearchResults), | 63 results_(new SearchResults), |
22 signed_in_(false), | 64 signed_in_(false), |
23 status_(STATUS_NORMAL) { | 65 status_(STATUS_NORMAL) { |
24 } | 66 } |
25 | 67 |
26 AppListModel::~AppListModel() { | 68 AppListModel::~AppListModel() { |
69 item_pages_.clear(); | |
27 } | 70 } |
28 | 71 |
29 void AppListModel::AddObserver(AppListModelObserver* observer) { | 72 void AppListModel::AddObserver(AppListModelObserver* observer) { |
30 observers_.AddObserver(observer); | 73 observers_.AddObserver(observer); |
31 } | 74 } |
32 | 75 |
33 void AppListModel::RemoveObserver(AppListModelObserver* observer) { | 76 void AppListModel::RemoveObserver(AppListModelObserver* observer) { |
34 observers_.RemoveObserver(observer); | 77 observers_.RemoveObserver(observer); |
35 } | 78 } |
36 | 79 |
(...skipping 18 matching lines...) Expand all Loading... | |
55 if (signed_in_ == signed_in) | 98 if (signed_in_ == signed_in) |
56 return; | 99 return; |
57 | 100 |
58 signed_in_ = signed_in; | 101 signed_in_ = signed_in; |
59 FOR_EACH_OBSERVER(AppListModelObserver, | 102 FOR_EACH_OBSERVER(AppListModelObserver, |
60 observers_, | 103 observers_, |
61 OnAppListModelSigninStatusChanged()); | 104 OnAppListModelSigninStatusChanged()); |
62 } | 105 } |
63 | 106 |
64 AppListItemModel* AppListModel::FindItem(const std::string& id) { | 107 AppListItemModel* AppListModel::FindItem(const std::string& id) { |
65 for (size_t i = 0; i < apps_->item_count(); ++i) { | 108 for (size_t page = 0; page < item_pages_.size(); ++page) { |
66 AppListItemModel* item = apps_->GetItemAt(i); | 109 AppItems* apps = app_items(page); |
67 if (item->id() == id) | 110 for (size_t i = 0; i < apps->item_count(); ++i) { |
68 return item; | 111 AppListItemModel* item = apps->GetItemAt(i); |
112 if (item->id() == id) | |
113 return item; | |
114 } | |
69 } | 115 } |
70 return NULL; | 116 return NULL; |
71 } | 117 } |
72 | 118 |
73 void AppListModel::AddItem(AppListItemModel* item) { | 119 |
120 AppListItemModel* AppListModel::GetItemAt(size_t page_idx, size_t item_idx) { | |
121 return app_items(page_idx)->GetItemAt(item_idx); | |
122 } | |
123 | |
124 size_t AppListModel::AddItem(AppListItemModel* item) { | |
125 CHECK(!FindItem(item->id())); | |
126 if (item_pages_.empty()) | |
127 return AddItemToPageAtIndex(item, 0, 0); | |
128 | |
129 // Currently items are kept sorted in the model such that for any item A | |
130 // on page 1 and item B on page 2, A.GetSortOrder() < B.GetSortOrder(). This | |
131 // also applies to any item A on a page that preceeds B on the same page. | |
74 std::string sort_order = item->GetSortOrder(); | 132 std::string sort_order = item->GetSortOrder(); |
75 // Note: ui::ListModel is not a sorted list. | 133 |
76 size_t index = 0; | 134 // First find the page that |item| belongs in. |
77 for (; index < apps_->item_count(); ++index) { | 135 size_t page_idx = 0; |
78 if (sort_order < apps_->GetItemAt(index)->GetSortOrder()) | 136 for (; page_idx < item_pages_.size() - 1; ++page_idx) { |
137 if (sort_order < app_items(page_idx + 1)->GetItemAt(0)->GetSortOrder()) | |
79 break; | 138 break; |
80 } | 139 } |
81 apps_->AddAt(index, item); | 140 // Items are kept sorted in a page by convention, but are not logically |
141 // gauranteed to be sorted (e.g. if an item's sort order were to change) | |
142 // so do a linear search instead of using std::find (also the number of items | |
143 // per page is small and a linear search is simpler). | |
144 AppItems* apps = app_items(page_idx); | |
145 size_t item_idx = 0; | |
146 for (; item_idx < apps->item_count(); ++item_idx) { | |
147 if (sort_order < apps->GetItemAt(item_idx)->GetSortOrder()) | |
148 break; | |
149 } | |
150 return AddItemToPageAtIndex(item, page_idx, item_idx); | |
151 } | |
152 | |
153 size_t AppListModel::AddItemToPage(AppListItemModel* item, size_t page_idx) { | |
154 CHECK(!FindItem(item->id())); | |
155 if (page_idx == item_pages_.size()) { | |
156 // OK to add an item to [number of app pages] + 1. | |
157 return AddItemToPageAtIndex(item, page_idx, 0); | |
158 } | |
159 std::string sort_order = item->GetSortOrder(); | |
160 AppItems* apps = app_items(page_idx); | |
161 size_t item_idx = 0; | |
162 for (; item_idx < apps->item_count(); ++item_idx) { | |
xiyuan
2013/10/18 23:04:08
Can we make this loop of insertion an item part of
stevenjb
2013/10/19 01:04:43
That sounds reasonable. I kind of wanted to keep t
| |
163 if (sort_order < apps->GetItemAt(item_idx)->GetSortOrder()) | |
164 break; | |
165 } | |
166 return AddItemToPageAtIndex(item, page_idx, item_idx); | |
82 } | 167 } |
83 | 168 |
84 void AppListModel::DeleteItem(const std::string& id) { | 169 void AppListModel::DeleteItem(const std::string& id) { |
85 for (size_t i = 0; i < apps_->item_count(); ++i) { | 170 for (size_t p = 0; p < item_pages_.size(); ++p) { |
86 AppListItemModel* item = apps_->GetItemAt(i); | 171 AppItems* apps = app_items(p); |
87 if (item->id() == id) { | 172 for (size_t i = 0; i < apps->item_count(); ++i) { |
88 apps_->DeleteAt(i); | 173 AppListItemModel* item = apps->GetItemAt(i); |
89 return; | 174 if (item->id() == id) { |
175 apps->DeleteAt(i); | |
176 if (apps->item_count() == 0) | |
177 RemovePage(p); | |
178 return; | |
179 } | |
90 } | 180 } |
91 } | 181 } |
92 } | 182 } |
93 | 183 |
184 void AppListModel::DeleteItemAt(size_t page_idx, size_t item_idx) { | |
185 AppItems* apps = app_items(page_idx); | |
186 apps->DeleteAt(item_idx); | |
187 if (apps->item_count() == 0) | |
188 RemovePage(page_idx); | |
189 } | |
190 | |
191 void AppListModel::MoveItem(size_t page_idx, size_t item_idx, | |
192 size_t target_page_idx, size_t target_item_idx) { | |
193 VLOG(2) << "MoveItem: " << page_idx << ", " << item_idx | |
194 << " -> " << target_page_idx << ", " << target_item_idx; | |
195 CHECK_LT(page_idx, item_pages_.size()); | |
196 CHECK_LE(item_idx, app_items(page_idx)->item_count()); | |
197 if (target_page_idx == page_idx) { | |
198 CHECK_LT(target_item_idx, app_items(page_idx)->item_count()); | |
199 app_items(page_idx)->Move(item_idx, target_item_idx); | |
200 // SetItemPosition will get called by ItemPage::ListItemMoved. | |
201 } else { | |
202 AppListItemModel* item = app_items(page_idx)->RemoveAt(item_idx); | |
203 if (target_page_idx >= item_pages_.size()) { | |
204 AddPage(); | |
205 CHECK_LT(target_page_idx, item_pages_.size()); | |
206 } | |
207 CHECK_LE(target_item_idx, app_items(target_page_idx)->item_count()); | |
208 AddItemToPageAtIndex(item, target_page_idx, target_item_idx); | |
209 } | |
210 } | |
211 | |
212 // static | |
213 size_t AppListModel::GetNumAppsPerPage() { | |
214 return kPreferredCols * kPreferredRows; | |
215 } | |
216 | |
217 size_t AppListModel::GetNumAppPages() const { | |
218 return item_pages_.size(); | |
219 } | |
220 | |
221 const AppListModel::AppItems& AppListModel::GetAppItemsForPage( | |
222 size_t page_idx) const { | |
223 CHECK_LT(page_idx, item_pages_.size()); | |
224 return *(item_pages_[page_idx]->app_items()); | |
225 } | |
226 | |
227 AppListModel::AppItems* AppListModel::app_items(size_t page_idx) { | |
228 CHECK_LT(page_idx, item_pages_.size()); | |
229 return item_pages_[page_idx]->app_items(); | |
230 } | |
231 | |
232 void AppListModel::AddPage() { | |
233 size_t page_idx = item_pages_.size(); | |
234 item_pages_.push_back(new ItemPage(this, page_idx)); | |
235 // Notify page added | |
236 } | |
237 | |
238 void AppListModel::RemovePage(size_t page_idx) { | |
239 item_pages_.erase(item_pages_.begin() + page_idx); | |
240 // Notify page removed | |
241 } | |
242 | |
243 size_t AppListModel::AddItemToPageAtIndex(AppListItemModel* item, | |
244 size_t page_idx, size_t item_idx) { | |
245 if (item_idx >= GetNumAppsPerPage()) { | |
246 ++page_idx; | |
247 item_idx = 0; | |
248 } | |
249 if (page_idx >= item_pages_.size()) | |
250 AddPage(); | |
251 | |
252 AppItems* apps = app_items(page_idx); | |
253 apps->AddAt(item_idx, item); | |
254 VLOG(2) << "AddItemToPage: " << item->id() | |
255 << " Page: " << page_idx << " Idx: " << item_idx | |
256 << " / " << apps->item_count(); | |
257 SetItemPosition(item, page_idx, item_idx); | |
258 if (apps->item_count() > GetNumAppsPerPage()) { | |
259 AppListItemModel* last_item = apps->RemoveAt(apps->item_count() - 1); | |
260 CHECK_EQ(GetNumAppsPerPage(), apps->item_count()); | |
261 if (++page_idx >= item_pages_.size()) | |
262 AddPage(); | |
263 AddItemToPageAtIndex(last_item, page_idx, 0); | |
264 } | |
265 return page_idx; | |
266 } | |
267 | |
268 void AppListModel::SetItemPosition(AppListItemModel* item, | |
269 size_t page_idx, size_t item_idx) { | |
270 VLOG(2) << " SetItemPosition: " << item->id() | |
271 << " Page: " << page_idx << " Idx: " << item_idx; | |
272 AppItems* apps = app_items(page_idx); | |
273 AppListItemModel* next = NULL; | |
274 if (item_idx + 1 < apps->item_count()) { | |
275 next = apps->GetItemAt(item_idx + 1); | |
276 } else if (page_idx < item_pages_.size() - 1) { | |
277 AppItems* next_apps = app_items(page_idx + 1); | |
278 CHECK(next_apps->item_count() > 0); | |
279 next = next_apps->GetItemAt(0); | |
280 } | |
281 AppListItemModel* prev = NULL; | |
282 if (item_idx > 0) { | |
283 prev = apps->GetItemAt(item_idx - 1); | |
284 } else if (page_idx > 0) { | |
285 AppItems* prev_apps = app_items(page_idx - 1); | |
286 CHECK(prev_apps->item_count() > 0); | |
287 prev = prev_apps->GetItemAt(prev_apps->item_count() - 1); | |
288 } | |
289 syncer::StringOrdinal new_position; | |
290 if (prev) { | |
291 new_position = next ? prev->position().CreateBetween(next->position()) | |
292 : prev->position().CreateAfter(); | |
293 } else { | |
294 new_position = next ? next->position().CreateBefore() | |
295 : syncer::StringOrdinal::CreateInitialOrdinal(); | |
296 } | |
297 item->set_position(new_position); | |
298 } | |
299 | |
94 } // namespace app_list | 300 } // namespace app_list |
OLD | NEW |