app_list_model.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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#include "ui/app_list/search_result.h" 14 15namespace app_list { 16 17AppListModel::AppListModel() 18 : top_level_item_list_(new AppListItemList), 19 search_box_(new SearchBoxModel), 20 results_(new SearchResults), 21 status_(STATUS_NORMAL) { 22 top_level_item_list_->AddObserver(this); 23} 24 25AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); } 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 = 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 58AppListFolderItem* 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 66AppListItem* 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 72AppListItem* 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 DCHECK(!item->IsInFolder() || item->folder_id() == folder_id); 78 DCHECK(item->GetItemType() != AppListFolderItem::kItemType); 79 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 80 DCHECK(!dest_folder->item_list()->FindItem(item->id())) 81 << "Already in folder: " << dest_folder->id(); 82 return AddItemToFolderItemAndNotify(dest_folder, item.Pass()); 83} 84 85const std::string AppListModel::MergeItems(const std::string& target_item_id, 86 const std::string& source_item_id) { 87 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id; 88 // Find the target item. 89 AppListItem* target_item = FindItem(target_item_id); 90 if (!target_item) { 91 LOG(ERROR) << "MergeItems: Target no longer exists."; 92 return ""; 93 } 94 CHECK(target_item->folder_id().empty()); 95 96 AppListItem* source_item = FindItem(source_item_id); 97 if (!source_item) { 98 LOG(ERROR) << "MergeItems: Source no longer exists."; 99 return ""; 100 } 101 102 // If the target item is a folder, just add the source item to it. 103 if (target_item->GetItemType() == AppListFolderItem::kItemType) { 104 AppListFolderItem* target_folder = 105 static_cast<AppListFolderItem*>(target_item); 106 if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) { 107 LOG(WARNING) << "MergeItems called with OEM folder as target"; 108 return ""; 109 } 110 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item); 111 source_item_ptr->set_position( 112 target_folder->item_list()->CreatePositionBefore( 113 syncer::StringOrdinal())); 114 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass()); 115 return target_folder->id(); 116 } 117 118 // Otherwise remove the source item and target item from their current 119 // location, they will become owned by the new folder. 120 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item); 121 CHECK(source_item_ptr); 122 scoped_ptr<AppListItem> target_item_ptr = 123 top_level_item_list_->RemoveItem(target_item_id); 124 CHECK(target_item_ptr); 125 126 // Create a new folder in the same location as the target item. 127 std::string new_folder_id = AppListFolderItem::GenerateId(); 128 DVLOG(2) << "Creating folder for merge: " << new_folder_id; 129 scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem( 130 new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL)); 131 new_folder_ptr->set_position(target_item_ptr->position()); 132 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>( 133 AddItemToItemListAndNotify(new_folder_ptr.Pass())); 134 135 // Add the items to the new folder. 136 target_item_ptr->set_position( 137 new_folder->item_list()->CreatePositionBefore( 138 syncer::StringOrdinal())); 139 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass()); 140 source_item_ptr->set_position( 141 new_folder->item_list()->CreatePositionBefore( 142 syncer::StringOrdinal())); 143 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass()); 144 145 return new_folder->id(); 146} 147 148void AppListModel::MoveItemToFolder(AppListItem* item, 149 const std::string& folder_id) { 150 DVLOG(2) << "MoveItemToFolder: " << folder_id 151 << " <- " << item->ToDebugString(); 152 if (item->folder_id() == folder_id) 153 return; 154 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 155 scoped_ptr<AppListItem> item_ptr = RemoveItem(item); 156 if (dest_folder) 157 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass()); 158 else 159 AddItemToItemListAndNotifyUpdate(item_ptr.Pass()); 160} 161 162bool AppListModel::MoveItemToFolderAt(AppListItem* item, 163 const std::string& folder_id, 164 syncer::StringOrdinal position) { 165 DVLOG(2) << "MoveItemToFolderAt: " << folder_id 166 << "[" << position.ToDebugString() << "]" 167 << " <- " << item->ToDebugString(); 168 if (item->folder_id() == folder_id) 169 return false; 170 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id()); 171 if (src_folder && 172 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) { 173 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source"; 174 return false; 175 } 176 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id); 177 scoped_ptr<AppListItem> item_ptr = RemoveItem(item); 178 if (dest_folder) { 179 item_ptr->set_position( 180 dest_folder->item_list()->CreatePositionBefore(position)); 181 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass()); 182 } else { 183 item_ptr->set_position( 184 top_level_item_list_->CreatePositionBefore(position)); 185 AddItemToItemListAndNotifyUpdate(item_ptr.Pass()); 186 } 187 return true; 188} 189 190void AppListModel::SetItemPosition(AppListItem* item, 191 const syncer::StringOrdinal& new_position) { 192 if (!item->IsInFolder()) { 193 top_level_item_list_->SetItemPosition(item, new_position); 194 // Note: this will trigger OnListItemMoved which will signal observers. 195 // (This is done this way because some View code still moves items within 196 // the item list directly). 197 return; 198 } 199 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 200 DCHECK(folder); 201 folder->item_list()->SetItemPosition(item, new_position); 202 FOR_EACH_OBSERVER(AppListModelObserver, 203 observers_, 204 OnAppListItemUpdated(item)); 205} 206 207void AppListModel::SetItemName(AppListItem* item, const std::string& name) { 208 item->SetName(name); 209 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString(); 210 FOR_EACH_OBSERVER(AppListModelObserver, 211 observers_, 212 OnAppListItemUpdated(item)); 213} 214 215void AppListModel::SetItemNameAndShortName(AppListItem* item, 216 const std::string& name, 217 const std::string& short_name) { 218 item->SetNameAndShortName(name, short_name); 219 DVLOG(2) << "AppListModel::SetItemNameAndShortName: " 220 << item->ToDebugString(); 221 FOR_EACH_OBSERVER(AppListModelObserver, 222 observers_, 223 OnAppListItemUpdated(item)); 224} 225 226void AppListModel::DeleteItem(const std::string& id) { 227 AppListItem* item = FindItem(id); 228 if (!item) 229 return; 230 if (!item->IsInFolder()) { 231 DCHECK_EQ(0u, item->ChildItemCount()) 232 << "Invalid call to DeleteItem for item with children: " << id; 233 FOR_EACH_OBSERVER(AppListModelObserver, 234 observers_, 235 OnAppListItemWillBeDeleted(item)); 236 top_level_item_list_->DeleteItem(id); 237 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted()); 238 return; 239 } 240 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 241 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString(); 242 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item); 243 DCHECK_EQ(item, child_item.get()); 244 FOR_EACH_OBSERVER(AppListModelObserver, 245 observers_, 246 OnAppListItemWillBeDeleted(item)); 247 child_item.reset(); // Deletes item. 248 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted()); 249} 250 251void AppListModel::NotifyExtensionPreferenceChanged() { 252 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) 253 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged(); 254} 255 256// Private methods 257 258void AppListModel::OnListItemMoved(size_t from_index, 259 size_t to_index, 260 AppListItem* item) { 261 FOR_EACH_OBSERVER(AppListModelObserver, 262 observers_, 263 OnAppListItemUpdated(item)); 264} 265 266AppListFolderItem* AppListModel::FindOrCreateFolderItem( 267 const std::string& folder_id) { 268 if (folder_id.empty()) 269 return NULL; 270 271 AppListFolderItem* dest_folder = FindFolderItem(folder_id); 272 if (dest_folder) 273 return dest_folder; 274 275 DVLOG(2) << "Creating new folder: " << folder_id; 276 scoped_ptr<AppListFolderItem> new_folder( 277 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL)); 278 new_folder->set_position( 279 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal())); 280 AppListItem* new_folder_item = 281 AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>()); 282 return static_cast<AppListFolderItem*>(new_folder_item); 283} 284 285AppListItem* AppListModel::AddItemToItemListAndNotify( 286 scoped_ptr<AppListItem> item_ptr) { 287 DCHECK(!item_ptr->IsInFolder()); 288 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass()); 289 FOR_EACH_OBSERVER(AppListModelObserver, 290 observers_, 291 OnAppListItemAdded(item)); 292 return item; 293} 294 295AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate( 296 scoped_ptr<AppListItem> item_ptr) { 297 DCHECK(!item_ptr->IsInFolder()); 298 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass()); 299 FOR_EACH_OBSERVER(AppListModelObserver, 300 observers_, 301 OnAppListItemUpdated(item)); 302 return item; 303} 304 305AppListItem* AppListModel::AddItemToFolderItemAndNotify( 306 AppListFolderItem* folder, 307 scoped_ptr<AppListItem> item_ptr) { 308 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass()); 309 item->set_folder_id(folder->id()); 310 FOR_EACH_OBSERVER(AppListModelObserver, 311 observers_, 312 OnAppListItemUpdated(item)); 313 return item; 314} 315 316scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) { 317 if (!item->IsInFolder()) 318 return top_level_item_list_->RemoveItem(item->id()); 319 320 AppListFolderItem* folder = FindFolderItem(item->folder_id()); 321 return RemoveItemFromFolder(folder, item); 322} 323 324scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder( 325 AppListFolderItem* folder, 326 AppListItem* item) { 327 std::string folder_id = folder->id(); 328 DCHECK_EQ(item->folder_id(), folder_id); 329 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id()); 330 result->set_folder_id(""); 331 if (folder->item_list()->item_count() == 0) { 332 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString(); 333 DeleteItem(folder_id); 334 } 335 return result.Pass(); 336} 337 338} // namespace app_list 339