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_model.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   observers_.RemoveObserver(observer);
     23 }
     24 
     25 AppListItemModel* AppListItemList::FindItem(const std::string& id) {
     26   for (size_t i = 0; i < app_list_items_.size(); ++i) {
     27     AppListItemModel* item = app_list_items_[i];
     28     if (item->id() == id)
     29       return item;
     30   }
     31   return NULL;
     32 }
     33 
     34 bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) {
     35   for (size_t i = 0; i < app_list_items_.size(); ++i) {
     36     AppListItemModel* item = app_list_items_[i];
     37     if (item->id() == id) {
     38       *index = i;
     39       return true;
     40     }
     41   }
     42   return false;
     43 }
     44 
     45 size_t AppListItemList::AddItem(AppListItemModel* item) {
     46   CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item)
     47         == app_list_items_.end());
     48   EnsureValidItemPosition(item);
     49   size_t index = GetItemSortOrderIndex(item->position(), item->id());
     50   app_list_items_.insert(app_list_items_.begin() + index, item);
     51   FOR_EACH_OBSERVER(AppListItemListObserver,
     52                     observers_,
     53                     OnListItemAdded(index, item));
     54   return index;
     55 }
     56 
     57 void AppListItemList::InsertItemAt(AppListItemModel* item, size_t index) {
     58   DCHECK_LE(index, item_count());
     59   if (item_count() == 0) {
     60     AddItem(item);
     61     return;
     62   }
     63 
     64   AppListItemModel* prev =
     65       index > 0 ? app_list_items_[index - 1] : NULL;
     66   AppListItemModel* next = index <= app_list_items_.size() - 1 ?
     67       app_list_items_[index] : NULL;
     68   CHECK_NE(prev, next);
     69 
     70   if (prev && next && prev->position().Equals(next->position()))
     71     prev = NULL;
     72 
     73   if (!prev)
     74     item->set_position(next->position().CreateBefore());
     75   else if (!next)
     76     item->set_position(prev->position().CreateAfter());
     77   else
     78     item->set_position(prev->position().CreateBetween(next->position()));
     79 
     80   app_list_items_.insert(app_list_items_.begin() + index, item);
     81 
     82   FOR_EACH_OBSERVER(AppListItemListObserver,
     83                     observers_,
     84                     OnListItemAdded(index, item));
     85 }
     86 
     87 void AppListItemList::DeleteItem(const std::string& id) {
     88   scoped_ptr<AppListItemModel> item = RemoveItem(id);
     89   // |item| will be deleted on destruction.
     90 }
     91 
     92 void AppListItemList::DeleteItemsByType(const char* type) {
     93   for (int i = static_cast<int>(app_list_items_.size()) - 1;
     94        i >= 0; --i) {
     95     AppListItemModel* item = app_list_items_[i];
     96     if (!type || item->GetAppType() == type)
     97       DeleteItemAt(i);
     98   }
     99 }
    100 
    101 scoped_ptr<AppListItemModel> AppListItemList::RemoveItem(
    102     const std::string& id) {
    103   size_t index;
    104   if (FindItemIndex(id, &index))
    105     return RemoveItemAt(index);
    106 
    107   return scoped_ptr<AppListItemModel>();
    108 }
    109 
    110 scoped_ptr<AppListItemModel> AppListItemList::RemoveItemAt(size_t index) {
    111   DCHECK_LT(index, item_count());
    112   AppListItemModel* item = app_list_items_[index];
    113   app_list_items_.weak_erase(app_list_items_.begin() + index);
    114   FOR_EACH_OBSERVER(AppListItemListObserver,
    115                     observers_,
    116                     OnListItemRemoved(index, item));
    117   return make_scoped_ptr<AppListItemModel>(item);
    118 }
    119 
    120 void AppListItemList::MoveItem(size_t from_index, size_t to_index) {
    121   DCHECK_LT(from_index, item_count());
    122   DCHECK_LT(to_index, item_count());
    123   if (from_index == to_index)
    124     return;
    125 
    126   AppListItemModel* target_item = app_list_items_[from_index];
    127   app_list_items_.weak_erase(app_list_items_.begin() + from_index);
    128   app_list_items_.insert(app_list_items_.begin() + to_index, target_item);
    129 
    130   // Update position
    131   AppListItemModel* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL;
    132   AppListItemModel* next = to_index < app_list_items_.size() - 1 ?
    133       app_list_items_[to_index + 1] : NULL;
    134   CHECK_NE(prev, next);
    135 
    136   // It is possible that items were added with the same ordinal. Rather than
    137   // resolving a potentially complicated chain of conflicts, just set the
    138   // ordinal before |next| (which will place it before both items).
    139   if (prev && next && prev->position().Equals(next->position()))
    140     prev = NULL;
    141 
    142   syncer::StringOrdinal new_position;
    143   if (!prev)
    144     new_position = next->position().CreateBefore();
    145   else if (!next)
    146     new_position = prev->position().CreateAfter();
    147   else
    148     new_position = prev->position().CreateBetween(next->position());
    149   VLOG(2) << "Move: " << target_item->position().ToDebugString()
    150           << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)")
    151           << " Next: " << (next ? next->position().ToDebugString() : "(none)")
    152           << " -> " << new_position.ToDebugString();
    153   target_item->set_position(new_position);
    154   FOR_EACH_OBSERVER(AppListItemListObserver,
    155                     observers_,
    156                     OnListItemMoved(from_index, to_index, target_item));
    157 }
    158 
    159 void AppListItemList::SetItemPosition(
    160     AppListItemModel* item,
    161     const syncer::StringOrdinal& new_position) {
    162   DCHECK(item);
    163   size_t from_index;
    164   if (!FindItemIndex(item->id(), &from_index)) {
    165     LOG(ERROR) << "SetItemPosition: Not in list: " << item->id().substr(0, 8);
    166     return;
    167   }
    168   DCHECK(app_list_items_[from_index] == item);
    169   // First check if the order would remain the same, in which case just update
    170   // the position.
    171   size_t to_index = GetItemSortOrderIndex(new_position, item->id());
    172   if (to_index == from_index) {
    173     VLOG(2) << "SetItemPosition: No change: " << item->id().substr(0, 8);
    174     item->set_position(new_position);
    175     return;
    176   }
    177   // Remove the item and get the updated to index.
    178   app_list_items_.weak_erase(app_list_items_.begin() + from_index);
    179   to_index = GetItemSortOrderIndex(new_position, item->id());
    180   VLOG(2) << "SetItemPosition: " << item->id().substr(0, 8)
    181           << " -> " << new_position.ToDebugString()
    182           << " From: " << from_index << " To: " << to_index;
    183   item->set_position(new_position);
    184   app_list_items_.insert(app_list_items_.begin() + to_index, item);
    185   FOR_EACH_OBSERVER(AppListItemListObserver,
    186                     observers_,
    187                     OnListItemMoved(from_index, to_index, item));
    188 }
    189 
    190 // AppListItemList private
    191 
    192 void AppListItemList::DeleteItemAt(size_t index) {
    193   scoped_ptr<AppListItemModel> item = RemoveItemAt(index);
    194   // |item| will be deleted on destruction.
    195 }
    196 
    197 void AppListItemList::EnsureValidItemPosition(AppListItemModel* item) {
    198   syncer::StringOrdinal position = item->position();
    199   if (position.IsValid())
    200     return;
    201   size_t nitems = app_list_items_.size();
    202   if (nitems == 0) {
    203     position = syncer::StringOrdinal::CreateInitialOrdinal();
    204   } else {
    205     position = app_list_items_[nitems - 1]->position().CreateAfter();
    206   }
    207   item->set_position(position);
    208 }
    209 
    210 size_t AppListItemList::GetItemSortOrderIndex(
    211     const syncer::StringOrdinal& position,
    212     const std::string& id) {
    213   DCHECK(position.IsValid());
    214   for (size_t index = 0; index < app_list_items_.size(); ++index) {
    215     if (position.LessThan(app_list_items_[index]->position()) ||
    216         (position.Equals(app_list_items_[index]->position()) &&
    217          (id < app_list_items_[index]->id()))) {
    218       return index;
    219     }
    220   }
    221   return app_list_items_.size();
    222 }
    223 
    224 }  // namespace app_list
    225