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.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 DCHECK(observers_.HasObserver(observer)); 23 observers_.RemoveObserver(observer); 24 } 25 26 AppListItem* AppListItemList::FindItem(const std::string& id) { 27 for (size_t i = 0; i < app_list_items_.size(); ++i) { 28 AppListItem* item = app_list_items_[i]; 29 if (item->id() == id) 30 return item; 31 } 32 return NULL; 33 } 34 35 bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) { 36 for (size_t i = 0; i < app_list_items_.size(); ++i) { 37 AppListItem* item = app_list_items_[i]; 38 if (item->id() == id) { 39 *index = i; 40 return true; 41 } 42 } 43 return false; 44 } 45 46 void AppListItemList::MoveItem(size_t from_index, size_t to_index) { 47 DCHECK_LT(from_index, item_count()); 48 DCHECK_LT(to_index, item_count()); 49 if (from_index == to_index) 50 return; 51 52 AppListItem* target_item = app_list_items_[from_index]; 53 DVLOG(2) << "MoveItem: " << from_index << " -> " << to_index << " [" 54 << target_item->position().ToDebugString() << "]"; 55 56 // Remove the target item 57 app_list_items_.weak_erase(app_list_items_.begin() + from_index); 58 59 // Update the position 60 AppListItem* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL; 61 AppListItem* next = 62 to_index < item_count() ? app_list_items_[to_index] : NULL; 63 CHECK_NE(prev, next); 64 syncer::StringOrdinal new_position; 65 if (!prev) { 66 new_position = next->position().CreateBefore(); 67 } else if (!next) { 68 new_position = prev->position().CreateAfter(); 69 } else { 70 // It is possible that items were added with the same ordinal. To 71 // successfully move the item we need to fix this. We do not try to fix this 72 // when an item is added in order to avoid possible edge cases with sync. 73 if (prev->position().Equals(next->position())) 74 FixItemPosition(to_index); 75 new_position = prev->position().CreateBetween(next->position()); 76 } 77 target_item->set_position(new_position); 78 79 DVLOG(2) << "Move: " 80 << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)") 81 << " Next: " << (next ? next->position().ToDebugString() : "(none)") 82 << " -> " << new_position.ToDebugString(); 83 84 // Insert the item and notify observers. 85 app_list_items_.insert(app_list_items_.begin() + to_index, target_item); 86 FOR_EACH_OBSERVER(AppListItemListObserver, 87 observers_, 88 OnListItemMoved(from_index, to_index, target_item)); 89 } 90 91 void AppListItemList::SetItemPosition(AppListItem* item, 92 syncer::StringOrdinal new_position) { 93 DCHECK(item); 94 size_t from_index; 95 if (!FindItemIndex(item->id(), &from_index)) { 96 LOG(ERROR) << "SetItemPosition: Not in list: " << item->id().substr(0, 8); 97 return; 98 } 99 DCHECK(app_list_items_[from_index] == item); 100 if (!new_position.IsValid()) { 101 size_t last_index = app_list_items_.size() - 1; 102 if (from_index == last_index) 103 return; // Already last item, do nothing. 104 new_position = app_list_items_[last_index]->position().CreateAfter(); 105 } 106 // First check if the order would remain the same, in which case just update 107 // the position. 108 size_t to_index = GetItemSortOrderIndex(new_position, item->id()); 109 if (to_index == from_index) { 110 DVLOG(2) << "SetItemPosition: No change: " << item->id().substr(0, 8); 111 item->set_position(new_position); 112 return; 113 } 114 // Remove the item and get the updated to index. 115 app_list_items_.weak_erase(app_list_items_.begin() + from_index); 116 to_index = GetItemSortOrderIndex(new_position, item->id()); 117 DVLOG(2) << "SetItemPosition: " << item->id().substr(0, 8) << " -> " 118 << new_position.ToDebugString() << " From: " << from_index 119 << " To: " << to_index; 120 item->set_position(new_position); 121 app_list_items_.insert(app_list_items_.begin() + to_index, item); 122 FOR_EACH_OBSERVER(AppListItemListObserver, 123 observers_, 124 OnListItemMoved(from_index, to_index, item)); 125 } 126 127 // AppListItemList private 128 129 syncer::StringOrdinal AppListItemList::CreatePositionBefore( 130 const syncer::StringOrdinal& position) { 131 if (app_list_items_.empty()) 132 return syncer::StringOrdinal::CreateInitialOrdinal(); 133 134 size_t nitems = app_list_items_.size(); 135 size_t index; 136 if (!position.IsValid()) { 137 index = nitems; 138 } else { 139 for (index = 0; index < nitems; ++index) { 140 if (!app_list_items_[index]->position().LessThan(position)) 141 break; 142 } 143 } 144 if (index == 0) 145 return app_list_items_[0]->position().CreateBefore(); 146 if (index == nitems) 147 return app_list_items_[nitems - 1]->position().CreateAfter(); 148 return app_list_items_[index - 1]->position().CreateBetween( 149 app_list_items_[index]->position()); 150 } 151 152 AppListItem* AppListItemList::AddItem(scoped_ptr<AppListItem> item_ptr) { 153 AppListItem* item = item_ptr.get(); 154 CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item) 155 == app_list_items_.end()); 156 EnsureValidItemPosition(item); 157 size_t index = GetItemSortOrderIndex(item->position(), item->id()); 158 app_list_items_.insert(app_list_items_.begin() + index, item_ptr.release()); 159 FOR_EACH_OBSERVER(AppListItemListObserver, 160 observers_, 161 OnListItemAdded(index, item)); 162 return item; 163 } 164 165 void AppListItemList::DeleteItem(const std::string& id) { 166 scoped_ptr<AppListItem> item = RemoveItem(id); 167 // |item| will be deleted on destruction. 168 } 169 170 scoped_ptr<AppListItem> AppListItemList::RemoveItem(const std::string& id) { 171 size_t index; 172 if (!FindItemIndex(id, &index)) 173 LOG(FATAL) << "RemoveItem: Not found: " << id; 174 return RemoveItemAt(index); 175 } 176 177 scoped_ptr<AppListItem> AppListItemList::RemoveItemAt(size_t index) { 178 CHECK_LT(index, item_count()); 179 AppListItem* item = app_list_items_[index]; 180 app_list_items_.weak_erase(app_list_items_.begin() + index); 181 FOR_EACH_OBSERVER(AppListItemListObserver, 182 observers_, 183 OnListItemRemoved(index, item)); 184 return make_scoped_ptr<AppListItem>(item); 185 } 186 187 void AppListItemList::DeleteItemAt(size_t index) { 188 scoped_ptr<AppListItem> item = RemoveItemAt(index); 189 // |item| will be deleted on destruction. 190 } 191 192 void AppListItemList::EnsureValidItemPosition(AppListItem* item) { 193 syncer::StringOrdinal position = item->position(); 194 if (position.IsValid()) 195 return; 196 size_t nitems = app_list_items_.size(); 197 if (nitems == 0) { 198 position = syncer::StringOrdinal::CreateInitialOrdinal(); 199 } else { 200 position = app_list_items_[nitems - 1]->position().CreateAfter(); 201 } 202 item->set_position(position); 203 } 204 205 size_t AppListItemList::GetItemSortOrderIndex( 206 const syncer::StringOrdinal& position, 207 const std::string& id) { 208 DCHECK(position.IsValid()); 209 for (size_t index = 0; index < app_list_items_.size(); ++index) { 210 if (position.LessThan(app_list_items_[index]->position()) || 211 (position.Equals(app_list_items_[index]->position()) && 212 (id < app_list_items_[index]->id()))) { 213 return index; 214 } 215 } 216 return app_list_items_.size(); 217 } 218 219 void AppListItemList::FixItemPosition(size_t index) { 220 DVLOG(1) << "FixItemPosition: " << index; 221 size_t nitems = item_count(); 222 DCHECK_LT(index, nitems); 223 DCHECK_GT(index, 0u); 224 // Update the position of |index| and any necessary subsequent items. 225 // First, find the next item that has a different position. 226 AppListItem* prev = app_list_items_[index - 1]; 227 size_t last_index = index + 1; 228 for (; last_index < nitems; ++last_index) { 229 if (!app_list_items_[last_index]->position().Equals(prev->position())) 230 break; 231 } 232 AppListItem* last = last_index < nitems ? app_list_items_[last_index] : NULL; 233 for (size_t i = index; i < last_index; ++i) { 234 AppListItem* cur = app_list_items_[i]; 235 if (last) 236 cur->set_position(prev->position().CreateBetween(last->position())); 237 else 238 cur->set_position(prev->position().CreateAfter()); 239 prev = cur; 240 } 241 FOR_EACH_OBSERVER(AppListItemListObserver, 242 observers_, 243 OnListItemMoved(index, index, app_list_items_[index])); 244 } 245 246 } // namespace app_list 247