Home | History | Annotate | Download | only in app_list
      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