1 // Copyright (c) 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. 4 5 #include "ui/app_list/app_list_item_list.h" 6 7 #include "ui/app_list/app_list_item_model.h" 8 9 namespace app_list { 10 11 AppListItemList::AppListItemList() { 12 } 13 14 AppListItemList::~AppListItemList() { 15 } 16 17 void AppListItemList::AddObserver(AppListItemListObserver* observer) { 18 observers_.AddObserver(observer); 19 } 20 21 void AppListItemList::RemoveObserver(AppListItemListObserver* observer) { 22 observers_.RemoveObserver(observer); 23 } 24 25 AppListItemModel* AppListItemList::FindItem(const std::string& id) { 26 for (size_t i = 0; i < app_list_items_.size(); ++i) { 27 AppListItemModel* item = app_list_items_[i]; 28 if (item->id() == id) 29 return item; 30 } 31 return NULL; 32 } 33 34 bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) { 35 for (size_t i = 0; i < app_list_items_.size(); ++i) { 36 AppListItemModel* item = app_list_items_[i]; 37 if (item->id() == id) { 38 *index = i; 39 return true; 40 } 41 } 42 return false; 43 } 44 45 size_t AppListItemList::AddItem(AppListItemModel* item) { 46 CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item) 47 == app_list_items_.end()); 48 EnsureValidItemPosition(item); 49 size_t index = GetItemSortOrderIndex(item->position(), item->id()); 50 app_list_items_.insert(app_list_items_.begin() + index, item); 51 FOR_EACH_OBSERVER(AppListItemListObserver, 52 observers_, 53 OnListItemAdded(index, item)); 54 return index; 55 } 56 57 void AppListItemList::InsertItemAt(AppListItemModel* item, size_t index) { 58 DCHECK_LE(index, item_count()); 59 if (item_count() == 0) { 60 AddItem(item); 61 return; 62 } 63 64 AppListItemModel* prev = 65 index > 0 ? app_list_items_[index - 1] : NULL; 66 AppListItemModel* next = index <= app_list_items_.size() - 1 ? 67 app_list_items_[index] : NULL; 68 CHECK_NE(prev, next); 69 70 if (prev && next && prev->position().Equals(next->position())) 71 prev = NULL; 72 73 if (!prev) 74 item->set_position(next->position().CreateBefore()); 75 else if (!next) 76 item->set_position(prev->position().CreateAfter()); 77 else 78 item->set_position(prev->position().CreateBetween(next->position())); 79 80 app_list_items_.insert(app_list_items_.begin() + index, item); 81 82 FOR_EACH_OBSERVER(AppListItemListObserver, 83 observers_, 84 OnListItemAdded(index, item)); 85 } 86 87 void AppListItemList::DeleteItem(const std::string& id) { 88 scoped_ptr<AppListItemModel> item = RemoveItem(id); 89 // |item| will be deleted on destruction. 90 } 91 92 void AppListItemList::DeleteItemsByType(const char* type) { 93 for (int i = static_cast<int>(app_list_items_.size()) - 1; 94 i >= 0; --i) { 95 AppListItemModel* item = app_list_items_[i]; 96 if (!type || item->GetAppType() == type) 97 DeleteItemAt(i); 98 } 99 } 100 101 scoped_ptr<AppListItemModel> AppListItemList::RemoveItem( 102 const std::string& id) { 103 size_t index; 104 if (FindItemIndex(id, &index)) 105 return RemoveItemAt(index); 106 107 return scoped_ptr<AppListItemModel>(); 108 } 109 110 scoped_ptr<AppListItemModel> AppListItemList::RemoveItemAt(size_t index) { 111 DCHECK_LT(index, item_count()); 112 AppListItemModel* item = app_list_items_[index]; 113 app_list_items_.weak_erase(app_list_items_.begin() + index); 114 FOR_EACH_OBSERVER(AppListItemListObserver, 115 observers_, 116 OnListItemRemoved(index, item)); 117 return make_scoped_ptr<AppListItemModel>(item); 118 } 119 120 void AppListItemList::MoveItem(size_t from_index, size_t to_index) { 121 DCHECK_LT(from_index, item_count()); 122 DCHECK_LT(to_index, item_count()); 123 if (from_index == to_index) 124 return; 125 126 AppListItemModel* target_item = app_list_items_[from_index]; 127 app_list_items_.weak_erase(app_list_items_.begin() + from_index); 128 app_list_items_.insert(app_list_items_.begin() + to_index, target_item); 129 130 // Update position 131 AppListItemModel* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL; 132 AppListItemModel* next = to_index < app_list_items_.size() - 1 ? 133 app_list_items_[to_index + 1] : NULL; 134 CHECK_NE(prev, next); 135 136 // It is possible that items were added with the same ordinal. Rather than 137 // resolving a potentially complicated chain of conflicts, just set the 138 // ordinal before |next| (which will place it before both items). 139 if (prev && next && prev->position().Equals(next->position())) 140 prev = NULL; 141 142 syncer::StringOrdinal new_position; 143 if (!prev) 144 new_position = next->position().CreateBefore(); 145 else if (!next) 146 new_position = prev->position().CreateAfter(); 147 else 148 new_position = prev->position().CreateBetween(next->position()); 149 VLOG(2) << "Move: " << target_item->position().ToDebugString() 150 << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)") 151 << " Next: " << (next ? next->position().ToDebugString() : "(none)") 152 << " -> " << new_position.ToDebugString(); 153 target_item->set_position(new_position); 154 FOR_EACH_OBSERVER(AppListItemListObserver, 155 observers_, 156 OnListItemMoved(from_index, to_index, target_item)); 157 } 158 159 void AppListItemList::SetItemPosition( 160 AppListItemModel* item, 161 const syncer::StringOrdinal& new_position) { 162 DCHECK(item); 163 size_t from_index; 164 if (!FindItemIndex(item->id(), &from_index)) { 165 LOG(ERROR) << "SetItemPosition: Not in list: " << item->id().substr(0, 8); 166 return; 167 } 168 DCHECK(app_list_items_[from_index] == item); 169 // First check if the order would remain the same, in which case just update 170 // the position. 171 size_t to_index = GetItemSortOrderIndex(new_position, item->id()); 172 if (to_index == from_index) { 173 VLOG(2) << "SetItemPosition: No change: " << item->id().substr(0, 8); 174 item->set_position(new_position); 175 return; 176 } 177 // Remove the item and get the updated to index. 178 app_list_items_.weak_erase(app_list_items_.begin() + from_index); 179 to_index = GetItemSortOrderIndex(new_position, item->id()); 180 VLOG(2) << "SetItemPosition: " << item->id().substr(0, 8) 181 << " -> " << new_position.ToDebugString() 182 << " From: " << from_index << " To: " << to_index; 183 item->set_position(new_position); 184 app_list_items_.insert(app_list_items_.begin() + to_index, item); 185 FOR_EACH_OBSERVER(AppListItemListObserver, 186 observers_, 187 OnListItemMoved(from_index, to_index, item)); 188 } 189 190 // AppListItemList private 191 192 void AppListItemList::DeleteItemAt(size_t index) { 193 scoped_ptr<AppListItemModel> item = RemoveItemAt(index); 194 // |item| will be deleted on destruction. 195 } 196 197 void AppListItemList::EnsureValidItemPosition(AppListItemModel* item) { 198 syncer::StringOrdinal position = item->position(); 199 if (position.IsValid()) 200 return; 201 size_t nitems = app_list_items_.size(); 202 if (nitems == 0) { 203 position = syncer::StringOrdinal::CreateInitialOrdinal(); 204 } else { 205 position = app_list_items_[nitems - 1]->position().CreateAfter(); 206 } 207 item->set_position(position); 208 } 209 210 size_t AppListItemList::GetItemSortOrderIndex( 211 const syncer::StringOrdinal& position, 212 const std::string& id) { 213 DCHECK(position.IsValid()); 214 for (size_t index = 0; index < app_list_items_.size(); ++index) { 215 if (position.LessThan(app_list_items_[index]->position()) || 216 (position.Equals(app_list_items_[index]->position()) && 217 (id < app_list_items_[index]->id()))) { 218 return index; 219 } 220 } 221 return app_list_items_.size(); 222 } 223 224 } // namespace app_list 225