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"
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
];
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
) {
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
)
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
;
62 to_index
< item_count() ? app_list_items_
[to_index
] : NULL
;
64 syncer::StringOrdinal new_position
;
66 new_position
= next
->position().CreateBefore();
68 new_position
= prev
->position().CreateAfter();
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
);
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
,
88 OnListItemMoved(from_index
, to_index
, target_item
));
91 void AppListItemList::SetItemPosition(AppListItem
* item
,
92 syncer::StringOrdinal new_position
) {
95 if (!FindItemIndex(item
->id(), &from_index
)) {
96 LOG(ERROR
) << "SetItemPosition: Not in list: " << item
->id().substr(0, 8);
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
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
);
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
,
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.
131 if (FindItemIndex(highlighted_id_
, &index
)) {
132 item_at(index
)->set_highlighted(false);
133 FOR_EACH_OBSERVER(AppListItemListObserver
,
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
144 item_at(index
)->set_highlighted(true);
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();
158 if (!position
.IsValid()) {
161 for (index
= 0; index
< nitems
; ++index
) {
162 if (!app_list_items_
[index
]->position().LessThan(position
))
167 return app_list_items_
[0]->position().CreateBefore();
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
,
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
,
190 OnAppListItemHighlight(index
, true));
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
) {
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
,
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())
226 size_t nitems
= app_list_items_
.size();
228 position
= syncer::StringOrdinal::CreateInitialOrdinal();
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()))) {
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()))
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
];
266 cur
->set_position(prev
->position().CreateBetween(last
->position()));
268 cur
->set_position(prev
->position().CreateAfter());
271 FOR_EACH_OBSERVER(AppListItemListObserver
,
273 OnListItemMoved(index
, index
, app_list_items_
[index
]));
276 } // namespace app_list