Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / app_list / app_list_item_list.cc
blob82a17c0b4a7fb6ca270320dd59d3d2816519ee1a
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.
5 #include "ui/app_list/app_list_item_list.h"
7 #include "ui/app_list/app_list_item.h"
9 namespace app_list {
11 AppListItemList::AppListItemList() {
14 AppListItemList::~AppListItemList() {
17 void AppListItemList::AddObserver(AppListItemListObserver* observer) {
18 observers_.AddObserver(observer);
21 void AppListItemList::RemoveObserver(AppListItemListObserver* observer) {
22 DCHECK(observers_.HasObserver(observer));
23 observers_.RemoveObserver(observer);
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;
32 return NULL;
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;
43 return false;
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;
52 AppListItem* target_item = app_list_items_[from_index];
53 DVLOG(2) << "MoveItem: " << from_index << " -> " << to_index << " ["
54 << target_item->position().ToDebugString() << "]";
56 // Remove the target item
57 app_list_items_.weak_erase(app_list_items_.begin() + from_index);
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());
77 target_item->set_position(new_position);
79 DVLOG(2) << "Move: "
80 << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)")
81 << " Next: " << (next ? next->position().ToDebugString() : "(none)")
82 << " -> " << new_position.ToDebugString();
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));
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;
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();
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;
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));
127 void AppListItemList::HighlightItemInstalledFromUI(const std::string& id) {
128 // Items within folders are not highlighted (apps are never installed to a
129 // folder initially). So just search the top-level list.
130 size_t index;
131 if (FindItemIndex(highlighted_id_, &index)) {
132 item_at(index)->set_highlighted(false);
133 FOR_EACH_OBSERVER(AppListItemListObserver,
134 observers_,
135 OnAppListItemHighlight(index, false));
137 highlighted_id_ = id;
138 if (!FindItemIndex(highlighted_id_, &index)) {
139 // If the item isin't in the app list yet, it will be highlighted later, in
140 // AddItem().
141 return;
144 item_at(index)->set_highlighted(true);
145 FOR_EACH_OBSERVER(
146 AppListItemListObserver, observers_, OnAppListItemHighlight(index, true));
149 // AppListItemList private
151 syncer::StringOrdinal AppListItemList::CreatePositionBefore(
152 const syncer::StringOrdinal& position) {
153 if (app_list_items_.empty())
154 return syncer::StringOrdinal::CreateInitialOrdinal();
156 size_t nitems = app_list_items_.size();
157 size_t index;
158 if (!position.IsValid()) {
159 index = nitems;
160 } else {
161 for (index = 0; index < nitems; ++index) {
162 if (!app_list_items_[index]->position().LessThan(position))
163 break;
166 if (index == 0)
167 return app_list_items_[0]->position().CreateBefore();
168 if (index == nitems)
169 return app_list_items_[nitems - 1]->position().CreateAfter();
170 return app_list_items_[index - 1]->position().CreateBetween(
171 app_list_items_[index]->position());
174 AppListItem* AppListItemList::AddItem(scoped_ptr<AppListItem> item_ptr) {
175 AppListItem* item = item_ptr.get();
176 CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item)
177 == app_list_items_.end());
178 EnsureValidItemPosition(item);
179 size_t index = GetItemSortOrderIndex(item->position(), item->id());
180 app_list_items_.insert(app_list_items_.begin() + index, item_ptr.release());
181 FOR_EACH_OBSERVER(AppListItemListObserver,
182 observers_,
183 OnListItemAdded(index, item));
185 if (item->id() == highlighted_id_) {
186 // Item not present when highlight requested, so highlight it now.
187 item->set_highlighted(true);
188 FOR_EACH_OBSERVER(AppListItemListObserver,
189 observers_,
190 OnAppListItemHighlight(index, true));
192 return item;
195 void AppListItemList::DeleteItem(const std::string& id) {
196 scoped_ptr<AppListItem> item = RemoveItem(id);
197 // |item| will be deleted on destruction.
200 scoped_ptr<AppListItem> AppListItemList::RemoveItem(const std::string& id) {
201 size_t index;
202 if (!FindItemIndex(id, &index))
203 LOG(FATAL) << "RemoveItem: Not found: " << id;
204 return RemoveItemAt(index);
207 scoped_ptr<AppListItem> AppListItemList::RemoveItemAt(size_t index) {
208 CHECK_LT(index, item_count());
209 AppListItem* item = app_list_items_[index];
210 app_list_items_.weak_erase(app_list_items_.begin() + index);
211 FOR_EACH_OBSERVER(AppListItemListObserver,
212 observers_,
213 OnListItemRemoved(index, item));
214 return make_scoped_ptr<AppListItem>(item);
217 void AppListItemList::DeleteItemAt(size_t index) {
218 scoped_ptr<AppListItem> item = RemoveItemAt(index);
219 // |item| will be deleted on destruction.
222 void AppListItemList::EnsureValidItemPosition(AppListItem* item) {
223 syncer::StringOrdinal position = item->position();
224 if (position.IsValid())
225 return;
226 size_t nitems = app_list_items_.size();
227 if (nitems == 0) {
228 position = syncer::StringOrdinal::CreateInitialOrdinal();
229 } else {
230 position = app_list_items_[nitems - 1]->position().CreateAfter();
232 item->set_position(position);
235 size_t AppListItemList::GetItemSortOrderIndex(
236 const syncer::StringOrdinal& position,
237 const std::string& id) {
238 DCHECK(position.IsValid());
239 for (size_t index = 0; index < app_list_items_.size(); ++index) {
240 if (position.LessThan(app_list_items_[index]->position()) ||
241 (position.Equals(app_list_items_[index]->position()) &&
242 (id < app_list_items_[index]->id()))) {
243 return index;
246 return app_list_items_.size();
249 void AppListItemList::FixItemPosition(size_t index) {
250 DVLOG(1) << "FixItemPosition: " << index;
251 size_t nitems = item_count();
252 DCHECK_LT(index, nitems);
253 DCHECK_GT(index, 0u);
254 // Update the position of |index| and any necessary subsequent items.
255 // First, find the next item that has a different position.
256 AppListItem* prev = app_list_items_[index - 1];
257 size_t last_index = index + 1;
258 for (; last_index < nitems; ++last_index) {
259 if (!app_list_items_[last_index]->position().Equals(prev->position()))
260 break;
262 AppListItem* last = last_index < nitems ? app_list_items_[last_index] : NULL;
263 for (size_t i = index; i < last_index; ++i) {
264 AppListItem* cur = app_list_items_[i];
265 if (last)
266 cur->set_position(prev->position().CreateBetween(last->position()));
267 else
268 cur->set_position(prev->position().CreateAfter());
269 prev = cur;
271 FOR_EACH_OBSERVER(AppListItemListObserver,
272 observers_,
273 OnListItemMoved(index, index, app_list_items_[index]));
276 } // namespace app_list