app_list_model.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 "ui/app_list/app_list_folder_item.h" 8#include "ui/app_list/app_list_item.h" 9#include "ui/app_list/app_list_model_observer.h" 10#include "ui/app_list/search_box_model.h" 11#include "ui/app_list/search_result.h" 12 13namespace app_list { 14 15AppListModel::AppListModel() 16 : item_list_(new AppListItemList), 17 search_box_(new SearchBoxModel), 18 results_(new SearchResults), 19 status_(STATUS_NORMAL) { 20 item_list_->AddObserver(this); 21} 22 23AppListModel::~AppListModel() { 24 item_list_->RemoveObserver(this); 25} 26 27void AppListModel::AddObserver(AppListModelObserver* observer) { 28 observers_.AddObserver(observer); 29} 30 31void AppListModel::RemoveObserver(AppListModelObserver* observer) { 32 observers_.RemoveObserver(observer); 33} 34 35void 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 45AppListItem* AppListModel::FindItem(const std::string& id) { 46 AppListItem* item = item_list_->FindItem(id); 47 if (item) 48 return item; 49 for (size_t i = 0; i < item_list_->item_count(); ++i) { 50 AppListItem* child_item = item_list_->item_at(i)->FindChildItem(id); 51 if (child_item) 52 return child_item; 53 } 54 return NULL; 55} 56 57AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) { 58 AppListItem* item = item_list_->FindItem(id); 59 if (item && item->GetItemType() == AppListFolderItem::kItemType) 60 return static_cast<AppListFolderItem*>(item); 61 DCHECK(!item); 62 return NULL; 63} 64 65AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) { 66 DCHECK(!item->IsInFolder()); 67 DCHECK(!item_list()->FindItem(item->id())); 68 return AddItemToItemListAndNotify(item.Pass()); 69} 70 71AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item, 72 const std::string& folder_id) { 73 if (folder_id.empty()) 74 return AddItem(item.Pass()); 75 DCHECK(!item->IsInFolder() || item->folder_id() == folder_id); 76 DCHECK(item->GetItemType() != AppListFolderItem::kItemType); 77 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 78 DCHECK(!dest_folder->item_list()->FindItem(item->id())); 79 return AddItemToFolderItemAndNotify(dest_folder, item.Pass()); 80} 81 82const std::string& AppListModel::MergeItems(const std::string& target_item_id, 83 const std::string& source_item_id) { 84 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id; 85 // First, remove the source item from the model. 86 scoped_ptr<AppListItem> source_item_ptr = 87 RemoveItem(FindItem(source_item_id)); 88 89 // Next, find the target item. 90 AppListItem* target_item = FindItem(target_item_id); 91 DCHECK(target_item); 92 DCHECK(target_item->folder_id().empty()); 93 94 // If the target item is a folder, just add |source_item| to it. 95 if (target_item->GetItemType() == AppListFolderItem::kItemType) { 96 AppListFolderItem* target_folder = 97 static_cast<AppListFolderItem*>(target_item); 98 source_item_ptr->set_position( 99 target_folder->item_list()->CreatePositionBefore( 100 syncer::StringOrdinal())); 101 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass()); 102 return target_folder->id(); 103 } 104 105 // Otherwise, remove the target item from |item_list_|, it will become owned 106 // by the new folder. 107 scoped_ptr<AppListItem> target_item_ptr = 108 item_list_->RemoveItem(target_item_id); 109 110 // Create a new folder in the same location as the target item. 111 scoped_ptr<AppListItem> new_folder_ptr( 112 new AppListFolderItem(AppListFolderItem::GenerateId())); 113 new_folder_ptr->set_position(target_item->position()); 114 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>( 115 AddItemToItemListAndNotify(new_folder_ptr.Pass())); 116 117 // Add the items to the new folder. 118 target_item_ptr->set_position( 119 new_folder->item_list()->CreatePositionBefore( 120 syncer::StringOrdinal())); 121 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass()); 122 source_item_ptr->set_position( 123 new_folder->item_list()->CreatePositionBefore( 124 syncer::StringOrdinal())); 125 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass()); 126 127 return new_folder->id(); 128} 129 130void AppListModel::MoveItemToFolder(AppListItem* item, 131 const std::string& folder_id) { 132 DVLOG(2) << "MoveItemToFolder: " << folder_id 133 << " <- " << item->ToDebugString(); 134 if (item->folder_id() == folder_id) 135 return; 136 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 137 scoped_ptr<AppListItem> item_ptr = RemoveItem(item); 138 if (dest_folder) 139 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass()); 140 else 141 AddItemToItemListAndNotifyUpdate(item_ptr.Pass()); 142} 143 144void AppListModel::MoveItemToFolderAt(AppListItem* item, 145 const std::string& folder_id, 146 syncer::StringOrdinal position) { 147 DVLOG(2) << "MoveItemToFolderAt: " << folder_id 148 << "[" << position.ToDebugString() << "]" 149 << " <- " << item->ToDebugString(); 150 if (item->folder_id() == folder_id) 151 return; 152 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 153 scoped_ptr<AppListItem> item_ptr = RemoveItem(item); 154 if (dest_folder) { 155 item_ptr->set_position( 156 dest_folder->item_list()->CreatePositionBefore(position)); 157 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass()); 158 } else { 159 item_ptr->set_position(item_list_->CreatePositionBefore(position)); 160 AddItemToItemListAndNotifyUpdate(item_ptr.Pass()); 161 } 162} 163 164void AppListModel::SetItemPosition(AppListItem* item, 165 const syncer::StringOrdinal& new_position) { 166 if (!item->IsInFolder()) { 167 item_list_->SetItemPosition(item, new_position); 168 // Note: this will trigger OnListItemMoved which will signal observers. 169 // (This is done this way because some View code still moves items within 170 // the item list directly). 171 return; 172 } 173 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 174 DCHECK(folder); 175 folder->item_list()->SetItemPosition(item, new_position); 176 FOR_EACH_OBSERVER(AppListModelObserver, 177 observers_, 178 OnAppListItemUpdated(item)); 179} 180 181void AppListModel::DeleteItem(const std::string& id) { 182 AppListItem* item = FindItem(id); 183 if (!item) 184 return; 185 if (!item->IsInFolder()) { 186 DCHECK_EQ(0u, item->ChildItemCount()) 187 << "Invalid call to DeleteItem for item with children: " << id; 188 FOR_EACH_OBSERVER(AppListModelObserver, 189 observers_, 190 OnAppListItemWillBeDeleted(item)); 191 item_list_->DeleteItem(id); 192 return; 193 } 194 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 195 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString(); 196 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item); 197 DCHECK_EQ(item, child_item.get()); 198 FOR_EACH_OBSERVER(AppListModelObserver, 199 observers_, 200 OnAppListItemWillBeDeleted(item)); 201 child_item.reset(); // Deletes item. 202} 203 204// Private methods 205 206void AppListModel::OnListItemMoved(size_t from_index, 207 size_t to_index, 208 AppListItem* item) { 209 FOR_EACH_OBSERVER(AppListModelObserver, 210 observers_, 211 OnAppListItemUpdated(item)); 212} 213 214AppListFolderItem* AppListModel::FindOrCreateFolderItem( 215 const std::string& folder_id) { 216 if (folder_id.empty()) 217 return NULL; 218 219 AppListFolderItem* dest_folder = FindFolderItem(folder_id); 220 if (dest_folder) 221 return dest_folder; 222 223 scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(folder_id)); 224 new_folder->set_position( 225 item_list_->CreatePositionBefore(syncer::StringOrdinal())); 226 AppListItem* new_folder_item = 227 AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>()); 228 return static_cast<AppListFolderItem*>(new_folder_item); 229} 230 231AppListItem* AppListModel::AddItemToItemListAndNotify( 232 scoped_ptr<AppListItem> item_ptr) { 233 DCHECK(!item_ptr->IsInFolder()); 234 AppListItem* item = item_list_->AddItem(item_ptr.Pass()); 235 FOR_EACH_OBSERVER(AppListModelObserver, 236 observers_, 237 OnAppListItemAdded(item)); 238 return item; 239} 240 241AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate( 242 scoped_ptr<AppListItem> item_ptr) { 243 DCHECK(!item_ptr->IsInFolder()); 244 AppListItem* item = item_list_->AddItem(item_ptr.Pass()); 245 FOR_EACH_OBSERVER(AppListModelObserver, 246 observers_, 247 OnAppListItemUpdated(item)); 248 return item; 249} 250 251AppListItem* AppListModel::AddItemToFolderItemAndNotify( 252 AppListFolderItem* folder, 253 scoped_ptr<AppListItem> item_ptr) { 254 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass()); 255 item->set_folder_id(folder->id()); 256 FOR_EACH_OBSERVER(AppListModelObserver, 257 observers_, 258 OnAppListItemUpdated(item)); 259 return item; 260} 261 262scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) { 263 if (!item->IsInFolder()) 264 return item_list_->RemoveItem(item->id()); 265 266 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 267 DCHECK(folder); 268 return RemoveItemFromFolder(folder, item); 269} 270 271scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder( 272 AppListFolderItem* folder, 273 AppListItem* item) { 274 std::string folder_id = folder->id(); 275 DCHECK_EQ(item->folder_id(), folder_id); 276 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id()); 277 result->set_folder_id(""); 278 if (folder->item_list()->item_count() == 0) { 279 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString(); 280 DeleteItem(folder_id); 281 } 282 return result.Pass(); 283} 284 285} // namespace app_list 286