Home | History | Annotate | Download | only in app_list
      1 // Copyright (c) 2012 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_model.h"
      6 
      7 #include <string>
      8 
      9 #include "ui/app_list/app_list_folder_item.h"
     10 #include "ui/app_list/app_list_item.h"
     11 #include "ui/app_list/app_list_model_observer.h"
     12 #include "ui/app_list/search_box_model.h"
     13 
     14 namespace app_list {
     15 
     16 AppListModel::AppListModel()
     17     : top_level_item_list_(new AppListItemList),
     18       search_box_(new SearchBoxModel),
     19       results_(new SearchResults),
     20       status_(STATUS_NORMAL),
     21       folders_enabled_(false) {
     22   top_level_item_list_->AddObserver(this);
     23 }
     24 
     25 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
     26 
     27 void AppListModel::AddObserver(AppListModelObserver* observer) {
     28   observers_.AddObserver(observer);
     29 }
     30 
     31 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
     32   observers_.RemoveObserver(observer);
     33 }
     34 
     35 void AppListModel::SetStatus(Status status) {
     36   if (status_ == status)
     37     return;
     38 
     39   status_ = status;
     40   FOR_EACH_OBSERVER(AppListModelObserver,
     41                     observers_,
     42                     OnAppListModelStatusChanged());
     43 }
     44 
     45 AppListItem* AppListModel::FindItem(const std::string& id) {
     46   AppListItem* item = top_level_item_list_->FindItem(id);
     47   if (item)
     48     return item;
     49   for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
     50     AppListItem* child_item =
     51         top_level_item_list_->item_at(i)->FindChildItem(id);
     52     if (child_item)
     53       return child_item;
     54   }
     55   return NULL;
     56 }
     57 
     58 AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) {
     59   AppListItem* item = top_level_item_list_->FindItem(id);
     60   if (item && item->GetItemType() == AppListFolderItem::kItemType)
     61     return static_cast<AppListFolderItem*>(item);
     62   DCHECK(!item);
     63   return NULL;
     64 }
     65 
     66 AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) {
     67   DCHECK(!item->IsInFolder());
     68   DCHECK(!top_level_item_list()->FindItem(item->id()));
     69   return AddItemToItemListAndNotify(item.Pass());
     70 }
     71 
     72 AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item,
     73                                            const std::string& folder_id) {
     74   if (folder_id.empty())
     75     return AddItem(item.Pass());
     76   DVLOG(2) << "AddItemToFolder: " << item->id() << ": " << folder_id;
     77   CHECK_NE(folder_id, item->folder_id());
     78   DCHECK_NE(AppListFolderItem::kItemType, item->GetItemType());
     79   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
     80   if (!dest_folder)
     81     return NULL;
     82   DCHECK(!dest_folder->item_list()->FindItem(item->id()))
     83       << "Already in folder: " << dest_folder->id();
     84   return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
     85 }
     86 
     87 const std::string AppListModel::MergeItems(const std::string& target_item_id,
     88                                            const std::string& source_item_id) {
     89   if (!folders_enabled()) {
     90     LOG(ERROR) << "MergeItems called with folders disabled.";
     91     return "";
     92   }
     93   DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
     94   // Find the target item.
     95   AppListItem* target_item = FindItem(target_item_id);
     96   if (!target_item) {
     97     LOG(ERROR) << "MergeItems: Target no longer exists.";
     98     return "";
     99   }
    100   CHECK(target_item->folder_id().empty());
    101 
    102   AppListItem* source_item = FindItem(source_item_id);
    103   if (!source_item) {
    104     LOG(ERROR) << "MergeItems: Source no longer exists.";
    105     return "";
    106   }
    107 
    108   // If the target item is a folder, just add the source item to it.
    109   if (target_item->GetItemType() == AppListFolderItem::kItemType) {
    110     AppListFolderItem* target_folder =
    111         static_cast<AppListFolderItem*>(target_item);
    112     if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
    113       LOG(WARNING) << "MergeItems called with OEM folder as target";
    114       return "";
    115     }
    116     scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
    117     source_item_ptr->set_position(
    118         target_folder->item_list()->CreatePositionBefore(
    119             syncer::StringOrdinal()));
    120     AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
    121     return target_folder->id();
    122   }
    123 
    124   // Otherwise remove the source item and target item from their current
    125   // location, they will become owned by the new folder.
    126   scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
    127   CHECK(source_item_ptr);
    128   scoped_ptr<AppListItem> target_item_ptr =
    129       top_level_item_list_->RemoveItem(target_item_id);
    130   CHECK(target_item_ptr);
    131 
    132   // Create a new folder in the same location as the target item.
    133   std::string new_folder_id = AppListFolderItem::GenerateId();
    134   DVLOG(2) << "Creating folder for merge: " << new_folder_id;
    135   scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
    136       new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
    137   new_folder_ptr->set_position(target_item_ptr->position());
    138   AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
    139       AddItemToItemListAndNotify(new_folder_ptr.Pass()));
    140 
    141   // Add the items to the new folder.
    142   target_item_ptr->set_position(
    143       new_folder->item_list()->CreatePositionBefore(
    144           syncer::StringOrdinal()));
    145   AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
    146   source_item_ptr->set_position(
    147       new_folder->item_list()->CreatePositionBefore(
    148           syncer::StringOrdinal()));
    149   AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
    150 
    151   return new_folder->id();
    152 }
    153 
    154 void AppListModel::MoveItemToFolder(AppListItem* item,
    155                                     const std::string& folder_id) {
    156   DVLOG(2) << "MoveItemToFolder: " << folder_id
    157            << " <- " << item->ToDebugString();
    158   if (item->folder_id() == folder_id)
    159     return;
    160   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
    161   scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
    162   if (dest_folder) {
    163     CHECK(!item->IsInFolder());
    164     AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
    165   } else {
    166     AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
    167   }
    168 }
    169 
    170 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
    171                                       const std::string& folder_id,
    172                                       syncer::StringOrdinal position) {
    173   DVLOG(2) << "MoveItemToFolderAt: " << folder_id
    174            << "[" << position.ToDebugString() << "]"
    175            << " <- " << item->ToDebugString();
    176   if (item->folder_id() == folder_id)
    177     return false;
    178   AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
    179   if (src_folder &&
    180       src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
    181     LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
    182     return false;
    183   }
    184   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
    185   scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
    186   if (dest_folder) {
    187     item_ptr->set_position(
    188         dest_folder->item_list()->CreatePositionBefore(position));
    189     AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
    190   } else {
    191     item_ptr->set_position(
    192         top_level_item_list_->CreatePositionBefore(position));
    193     AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
    194   }
    195   return true;
    196 }
    197 
    198 void AppListModel::SetItemPosition(AppListItem* item,
    199                                    const syncer::StringOrdinal& new_position) {
    200   if (!item->IsInFolder()) {
    201     top_level_item_list_->SetItemPosition(item, new_position);
    202     // Note: this will trigger OnListItemMoved which will signal observers.
    203     // (This is done this way because some View code still moves items within
    204     // the item list directly).
    205     return;
    206   }
    207   AppListFolderItem* folder = FindFolderItem(item->folder_id());
    208   DCHECK(folder);
    209   folder->item_list()->SetItemPosition(item, new_position);
    210   FOR_EACH_OBSERVER(AppListModelObserver,
    211                     observers_,
    212                     OnAppListItemUpdated(item));
    213 }
    214 
    215 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
    216   item->SetName(name);
    217   DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
    218   FOR_EACH_OBSERVER(AppListModelObserver,
    219                     observers_,
    220                     OnAppListItemUpdated(item));
    221 }
    222 
    223 void AppListModel::SetItemNameAndShortName(AppListItem* item,
    224                                            const std::string& name,
    225                                            const std::string& short_name) {
    226   item->SetNameAndShortName(name, short_name);
    227   DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
    228            << item->ToDebugString();
    229   FOR_EACH_OBSERVER(AppListModelObserver,
    230                     observers_,
    231                     OnAppListItemUpdated(item));
    232 }
    233 
    234 void AppListModel::DeleteItem(const std::string& id) {
    235   AppListItem* item = FindItem(id);
    236   if (!item)
    237     return;
    238   if (!item->IsInFolder()) {
    239     DCHECK_EQ(0u, item->ChildItemCount())
    240         << "Invalid call to DeleteItem for item with children: " << id;
    241     FOR_EACH_OBSERVER(AppListModelObserver,
    242                       observers_,
    243                       OnAppListItemWillBeDeleted(item));
    244     top_level_item_list_->DeleteItem(id);
    245     FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
    246     return;
    247   }
    248   AppListFolderItem* folder = FindFolderItem(item->folder_id());
    249   DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
    250   scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
    251   DCHECK_EQ(item, child_item.get());
    252   FOR_EACH_OBSERVER(AppListModelObserver,
    253                     observers_,
    254                     OnAppListItemWillBeDeleted(item));
    255   child_item.reset();  // Deletes item.
    256   FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
    257 }
    258 
    259 void AppListModel::NotifyExtensionPreferenceChanged() {
    260   for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
    261     top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
    262 }
    263 
    264 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
    265   folders_enabled_ = folders_enabled;
    266   if (folders_enabled)
    267     return;
    268   // Remove child items from folders.
    269   std::vector<std::string> folder_ids;
    270   for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
    271     AppListItem* item = top_level_item_list_->item_at(i);
    272     if (item->GetItemType() != AppListFolderItem::kItemType)
    273       continue;
    274     AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
    275     if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
    276       continue;  // Do not remove OEM folders.
    277     while (folder->item_list()->item_count()) {
    278       scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
    279       child->set_folder_id("");
    280       AddItemToItemListAndNotifyUpdate(child.Pass());
    281     }
    282     folder_ids.push_back(folder->id());
    283   }
    284   // Delete folders.
    285   for (size_t i = 0; i < folder_ids.size(); ++i)
    286     DeleteItem(folder_ids[i]);
    287 }
    288 
    289 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
    290     SearchResults* results,
    291     SearchResult::DisplayType display_type,
    292     size_t max_results) {
    293   std::vector<SearchResult*> matches;
    294   for (size_t i = 0; i < results->item_count(); ++i) {
    295     SearchResult* item = results->GetItemAt(i);
    296     if (item->display_type() == display_type) {
    297       matches.push_back(item);
    298       if (matches.size() == max_results)
    299         break;
    300     }
    301   }
    302   return matches;
    303 }
    304 
    305 // Private methods
    306 
    307 void AppListModel::OnListItemMoved(size_t from_index,
    308                                    size_t to_index,
    309                                    AppListItem* item) {
    310   FOR_EACH_OBSERVER(AppListModelObserver,
    311                     observers_,
    312                     OnAppListItemUpdated(item));
    313 }
    314 
    315 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
    316     const std::string& folder_id) {
    317   if (folder_id.empty())
    318     return NULL;
    319 
    320   AppListFolderItem* dest_folder = FindFolderItem(folder_id);
    321   if (dest_folder)
    322     return dest_folder;
    323 
    324   if (!folders_enabled()) {
    325     LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
    326     return NULL;
    327   }
    328 
    329   DVLOG(2) << "Creating new folder: " << folder_id;
    330   scoped_ptr<AppListFolderItem> new_folder(
    331       new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
    332   new_folder->set_position(
    333       top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
    334   AppListItem* new_folder_item =
    335       AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>());
    336   return static_cast<AppListFolderItem*>(new_folder_item);
    337 }
    338 
    339 AppListItem* AppListModel::AddItemToItemListAndNotify(
    340     scoped_ptr<AppListItem> item_ptr) {
    341   DCHECK(!item_ptr->IsInFolder());
    342   AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
    343   FOR_EACH_OBSERVER(AppListModelObserver,
    344                     observers_,
    345                     OnAppListItemAdded(item));
    346   return item;
    347 }
    348 
    349 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
    350     scoped_ptr<AppListItem> item_ptr) {
    351   DCHECK(!item_ptr->IsInFolder());
    352   AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
    353   FOR_EACH_OBSERVER(AppListModelObserver,
    354                     observers_,
    355                     OnAppListItemUpdated(item));
    356   return item;
    357 }
    358 
    359 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
    360     AppListFolderItem* folder,
    361     scoped_ptr<AppListItem> item_ptr) {
    362   CHECK_NE(folder->id(), item_ptr->folder_id());
    363   AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
    364   item->set_folder_id(folder->id());
    365   FOR_EACH_OBSERVER(AppListModelObserver,
    366                     observers_,
    367                     OnAppListItemUpdated(item));
    368   return item;
    369 }
    370 
    371 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
    372   if (!item->IsInFolder())
    373     return top_level_item_list_->RemoveItem(item->id());
    374 
    375   AppListFolderItem* folder = FindFolderItem(item->folder_id());
    376   return RemoveItemFromFolder(folder, item);
    377 }
    378 
    379 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
    380     AppListFolderItem* folder,
    381     AppListItem* item) {
    382   std::string folder_id = folder->id();
    383   CHECK_EQ(item->folder_id(), folder_id);
    384   scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
    385   result->set_folder_id("");
    386   if (folder->item_list()->item_count() == 0) {
    387     DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
    388     DeleteItem(folder_id);
    389   }
    390   return result.Pass();
    391 }
    392 
    393 }  // namespace app_list
    394