// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/app_list/app_list_item_list.h" #include "ui/app_list/app_list_item.h" namespace app_list { AppListItemList::AppListItemList() { } AppListItemList::~AppListItemList() { } void AppListItemList::AddObserver(AppListItemListObserver* observer) { observers_.AddObserver(observer); } void AppListItemList::RemoveObserver(AppListItemListObserver* observer) { DCHECK(observers_.HasObserver(observer)); observers_.RemoveObserver(observer); } AppListItem* AppListItemList::FindItem(const std::string& id) { for (size_t i = 0; i < app_list_items_.size(); ++i) { AppListItem* item = app_list_items_[i]; if (item->id() == id) return item; } return NULL; } bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) { for (size_t i = 0; i < app_list_items_.size(); ++i) { AppListItem* item = app_list_items_[i]; if (item->id() == id) { *index = i; return true; } } return false; } void AppListItemList::MoveItem(size_t from_index, size_t to_index) { DCHECK_LT(from_index, item_count()); DCHECK_LT(to_index, item_count()); if (from_index == to_index) return; AppListItem* target_item = app_list_items_[from_index]; DVLOG(2) << "MoveItem: " << from_index << " -> " << to_index << " [" << target_item->position().ToDebugString() << "]"; // Remove the target item app_list_items_.weak_erase(app_list_items_.begin() + from_index); // Update the position AppListItem* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL; AppListItem* next = to_index < item_count() ? app_list_items_[to_index] : NULL; CHECK_NE(prev, next); syncer::StringOrdinal new_position; if (!prev) { new_position = next->position().CreateBefore(); } else if (!next) { new_position = prev->position().CreateAfter(); } else { // It is possible that items were added with the same ordinal. To // successfully move the item we need to fix this. We do not try to fix this // when an item is added in order to avoid possible edge cases with sync. if (prev->position().Equals(next->position())) FixItemPosition(to_index); new_position = prev->position().CreateBetween(next->position()); } target_item->set_position(new_position); DVLOG(2) << "Move: " << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)") << " Next: " << (next ? next->position().ToDebugString() : "(none)") << " -> " << new_position.ToDebugString(); // Insert the item and notify observers. app_list_items_.insert(app_list_items_.begin() + to_index, target_item); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnListItemMoved(from_index, to_index, target_item)); } void AppListItemList::SetItemPosition(AppListItem* item, syncer::StringOrdinal new_position) { DCHECK(item); size_t from_index; if (!FindItemIndex(item->id(), &from_index)) { LOG(ERROR) << "SetItemPosition: Not in list: " << item->id().substr(0, 8); return; } DCHECK(app_list_items_[from_index] == item); if (!new_position.IsValid()) { size_t last_index = app_list_items_.size() - 1; if (from_index == last_index) return; // Already last item, do nothing. new_position = app_list_items_[last_index]->position().CreateAfter(); } // First check if the order would remain the same, in which case just update // the position. size_t to_index = GetItemSortOrderIndex(new_position, item->id()); if (to_index == from_index) { DVLOG(2) << "SetItemPosition: No change: " << item->id().substr(0, 8); item->set_position(new_position); return; } // Remove the item and get the updated to index. app_list_items_.weak_erase(app_list_items_.begin() + from_index); to_index = GetItemSortOrderIndex(new_position, item->id()); DVLOG(2) << "SetItemPosition: " << item->id().substr(0, 8) << " -> " << new_position.ToDebugString() << " From: " << from_index << " To: " << to_index; item->set_position(new_position); app_list_items_.insert(app_list_items_.begin() + to_index, item); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnListItemMoved(from_index, to_index, item)); } void AppListItemList::HighlightItemInstalledFromUI(const std::string& id) { // Items within folders are not highlighted (apps are never installed to a // folder initially). So just search the top-level list. size_t index; if (FindItemIndex(highlighted_id_, &index)) { item_at(index)->set_highlighted(false); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnAppListItemHighlight(index, false)); } highlighted_id_ = id; if (!FindItemIndex(highlighted_id_, &index)) { // If the item isin't in the app list yet, it will be highlighted later, in // AddItem(). return; } item_at(index)->set_highlighted(true); FOR_EACH_OBSERVER( AppListItemListObserver, observers_, OnAppListItemHighlight(index, true)); } // AppListItemList private syncer::StringOrdinal AppListItemList::CreatePositionBefore( const syncer::StringOrdinal& position) { if (app_list_items_.empty()) return syncer::StringOrdinal::CreateInitialOrdinal(); size_t nitems = app_list_items_.size(); size_t index; if (!position.IsValid()) { index = nitems; } else { for (index = 0; index < nitems; ++index) { if (!app_list_items_[index]->position().LessThan(position)) break; } } if (index == 0) return app_list_items_[0]->position().CreateBefore(); if (index == nitems) return app_list_items_[nitems - 1]->position().CreateAfter(); return app_list_items_[index - 1]->position().CreateBetween( app_list_items_[index]->position()); } AppListItem* AppListItemList::AddItem(scoped_ptr item_ptr) { AppListItem* item = item_ptr.get(); CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item) == app_list_items_.end()); EnsureValidItemPosition(item); size_t index = GetItemSortOrderIndex(item->position(), item->id()); app_list_items_.insert(app_list_items_.begin() + index, item_ptr.release()); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnListItemAdded(index, item)); if (item->id() == highlighted_id_) { // Item not present when highlight requested, so highlight it now. item->set_highlighted(true); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnAppListItemHighlight(index, true)); } return item; } void AppListItemList::DeleteItem(const std::string& id) { scoped_ptr item = RemoveItem(id); // |item| will be deleted on destruction. } scoped_ptr AppListItemList::RemoveItem(const std::string& id) { size_t index; if (!FindItemIndex(id, &index)) LOG(FATAL) << "RemoveItem: Not found: " << id; return RemoveItemAt(index); } scoped_ptr AppListItemList::RemoveItemAt(size_t index) { CHECK_LT(index, item_count()); AppListItem* item = app_list_items_[index]; app_list_items_.weak_erase(app_list_items_.begin() + index); FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnListItemRemoved(index, item)); return make_scoped_ptr(item); } void AppListItemList::DeleteItemAt(size_t index) { scoped_ptr item = RemoveItemAt(index); // |item| will be deleted on destruction. } void AppListItemList::EnsureValidItemPosition(AppListItem* item) { syncer::StringOrdinal position = item->position(); if (position.IsValid()) return; size_t nitems = app_list_items_.size(); if (nitems == 0) { position = syncer::StringOrdinal::CreateInitialOrdinal(); } else { position = app_list_items_[nitems - 1]->position().CreateAfter(); } item->set_position(position); } size_t AppListItemList::GetItemSortOrderIndex( const syncer::StringOrdinal& position, const std::string& id) { DCHECK(position.IsValid()); for (size_t index = 0; index < app_list_items_.size(); ++index) { if (position.LessThan(app_list_items_[index]->position()) || (position.Equals(app_list_items_[index]->position()) && (id < app_list_items_[index]->id()))) { return index; } } return app_list_items_.size(); } void AppListItemList::FixItemPosition(size_t index) { DVLOG(1) << "FixItemPosition: " << index; size_t nitems = item_count(); DCHECK_LT(index, nitems); DCHECK_GT(index, 0u); // Update the position of |index| and any necessary subsequent items. // First, find the next item that has a different position. AppListItem* prev = app_list_items_[index - 1]; size_t last_index = index + 1; for (; last_index < nitems; ++last_index) { if (!app_list_items_[last_index]->position().Equals(prev->position())) break; } AppListItem* last = last_index < nitems ? app_list_items_[last_index] : NULL; for (size_t i = index; i < last_index; ++i) { AppListItem* cur = app_list_items_[i]; if (last) cur->set_position(prev->position().CreateBetween(last->position())); else cur->set_position(prev->position().CreateAfter()); prev = cur; } FOR_EACH_OBSERVER(AppListItemListObserver, observers_, OnListItemMoved(index, index, app_list_items_[index])); } } // namespace app_list