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.
5 #include "ui/app_list/app_list_model.h"
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"
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);
25 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
27 void AppListModel::AddObserver(AppListModelObserver* observer) {
28 observers_.AddObserver(observer);
31 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
32 observers_.RemoveObserver(observer);
35 void AppListModel::SetStatus(Status status) {
36 if (status_ == status)
40 FOR_EACH_OBSERVER(AppListModelObserver,
42 OnAppListModelStatusChanged());
45 AppListItem* AppListModel::FindItem(const std::string& id) {
46 AppListItem* item = top_level_item_list_->FindItem(id);
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);
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);
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());
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 DCHECK(!item->IsInFolder() || item->folder_id() == folder_id);
78 DCHECK(item->GetItemType() != AppListFolderItem::kItemType);
79 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
82 DCHECK(!dest_folder->item_list()->FindItem(item->id()))
83 << "Already in folder: " << dest_folder->id();
84 return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
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.";
93 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
94 // Find the target item.
95 AppListItem* target_item = FindItem(target_item_id);
97 LOG(ERROR) << "MergeItems: Target no longer exists.";
100 CHECK(target_item->folder_id().empty());
102 AppListItem* source_item = FindItem(source_item_id);
104 LOG(ERROR) << "MergeItems: Source no longer exists.";
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";
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();
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);
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()));
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());
151 return new_folder->id();
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)
160 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
161 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
163 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
165 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
168 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
169 const std::string& folder_id,
170 syncer::StringOrdinal position) {
171 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
172 << "[" << position.ToDebugString() << "]"
173 << " <- " << item->ToDebugString();
174 if (item->folder_id() == folder_id)
176 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
178 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
179 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
182 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
183 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
185 item_ptr->set_position(
186 dest_folder->item_list()->CreatePositionBefore(position));
187 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
189 item_ptr->set_position(
190 top_level_item_list_->CreatePositionBefore(position));
191 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
196 void AppListModel::SetItemPosition(AppListItem* item,
197 const syncer::StringOrdinal& new_position) {
198 if (!item->IsInFolder()) {
199 top_level_item_list_->SetItemPosition(item, new_position);
200 // Note: this will trigger OnListItemMoved which will signal observers.
201 // (This is done this way because some View code still moves items within
202 // the item list directly).
205 AppListFolderItem* folder = FindFolderItem(item->folder_id());
207 folder->item_list()->SetItemPosition(item, new_position);
208 FOR_EACH_OBSERVER(AppListModelObserver,
210 OnAppListItemUpdated(item));
213 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
215 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
216 FOR_EACH_OBSERVER(AppListModelObserver,
218 OnAppListItemUpdated(item));
221 void AppListModel::SetItemNameAndShortName(AppListItem* item,
222 const std::string& name,
223 const std::string& short_name) {
224 item->SetNameAndShortName(name, short_name);
225 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
226 << item->ToDebugString();
227 FOR_EACH_OBSERVER(AppListModelObserver,
229 OnAppListItemUpdated(item));
232 void AppListModel::DeleteItem(const std::string& id) {
233 AppListItem* item = FindItem(id);
236 if (!item->IsInFolder()) {
237 DCHECK_EQ(0u, item->ChildItemCount())
238 << "Invalid call to DeleteItem for item with children: " << id;
239 FOR_EACH_OBSERVER(AppListModelObserver,
241 OnAppListItemWillBeDeleted(item));
242 top_level_item_list_->DeleteItem(id);
243 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
246 AppListFolderItem* folder = FindFolderItem(item->folder_id());
247 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
248 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
249 DCHECK_EQ(item, child_item.get());
250 FOR_EACH_OBSERVER(AppListModelObserver,
252 OnAppListItemWillBeDeleted(item));
253 child_item.reset(); // Deletes item.
254 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
257 void AppListModel::NotifyExtensionPreferenceChanged() {
258 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
259 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
262 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
263 folders_enabled_ = folders_enabled;
266 // Remove child items from folders.
267 std::vector<std::string> folder_ids;
268 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
269 AppListItem* item = top_level_item_list_->item_at(i);
270 if (item->GetItemType() != AppListFolderItem::kItemType)
272 AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
273 if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
274 continue; // Do not remove OEM folders.
275 while (folder->item_list()->item_count()) {
276 scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
277 child->set_folder_id("");
278 AddItemToItemListAndNotifyUpdate(child.Pass());
280 folder_ids.push_back(folder->id());
283 for (size_t i = 0; i < folder_ids.size(); ++i)
284 DeleteItem(folder_ids[i]);
287 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
288 SearchResults* results,
289 SearchResult::DisplayType display_type,
290 size_t max_results) {
291 std::vector<SearchResult*> matches;
292 for (size_t i = 0; i < results->item_count(); ++i) {
293 SearchResult* item = results->GetItemAt(i);
294 if (item->display_type() == display_type) {
295 matches.push_back(item);
296 if (matches.size() == max_results)
305 void AppListModel::OnListItemMoved(size_t from_index,
308 FOR_EACH_OBSERVER(AppListModelObserver,
310 OnAppListItemUpdated(item));
313 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
314 const std::string& folder_id) {
315 if (folder_id.empty())
318 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
322 if (!folders_enabled()) {
323 LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
327 DVLOG(2) << "Creating new folder: " << folder_id;
328 scoped_ptr<AppListFolderItem> new_folder(
329 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
330 new_folder->set_position(
331 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
332 AppListItem* new_folder_item =
333 AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>());
334 return static_cast<AppListFolderItem*>(new_folder_item);
337 AppListItem* AppListModel::AddItemToItemListAndNotify(
338 scoped_ptr<AppListItem> item_ptr) {
339 DCHECK(!item_ptr->IsInFolder());
340 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
341 FOR_EACH_OBSERVER(AppListModelObserver,
343 OnAppListItemAdded(item));
347 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
348 scoped_ptr<AppListItem> item_ptr) {
349 DCHECK(!item_ptr->IsInFolder());
350 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
351 FOR_EACH_OBSERVER(AppListModelObserver,
353 OnAppListItemUpdated(item));
357 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
358 AppListFolderItem* folder,
359 scoped_ptr<AppListItem> item_ptr) {
360 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
361 item->set_folder_id(folder->id());
362 FOR_EACH_OBSERVER(AppListModelObserver,
364 OnAppListItemUpdated(item));
368 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
369 if (!item->IsInFolder())
370 return top_level_item_list_->RemoveItem(item->id());
372 AppListFolderItem* folder = FindFolderItem(item->folder_id());
373 return RemoveItemFromFolder(folder, item);
376 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
377 AppListFolderItem* folder,
379 std::string folder_id = folder->id();
380 DCHECK_EQ(item->folder_id(), folder_id);
381 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
382 result->set_folder_id("");
383 if (folder->item_list()->item_count() == 0) {
384 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
385 DeleteItem(folder_id);
387 return result.Pass();
390 } // namespace app_list