Chromium Code Reviews| 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 |