Blink roll 171798:171837
[chromium-blink-merge.git] / ui / app_list / app_list_model.cc
blobf72d73f510bc6aee168995061faa4d7719e483ed
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"
7 #include <string>
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"
15 namespace app_list {
17 AppListModel::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);
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)
37 return;
39 status_ = status;
40 FOR_EACH_OBSERVER(AppListModelObserver,
41 observers_,
42 OnAppListModelStatusChanged());
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;
55 return NULL;
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;
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);
80 DCHECK(!dest_folder->item_list()->FindItem(item->id()))
81 << "Already in folder: " << dest_folder->id();
82 return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
85 const 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 "";
94 CHECK(target_item->folder_id().empty());
96 AppListItem* source_item = FindItem(source_item_id);
97 if (!source_item) {
98 LOG(ERROR) << "MergeItems: Source no longer exists.";
99 return "";
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 "";
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();
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);
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()));
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());
145 return new_folder->id();
148 void 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());
162 bool 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;
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());
187 return true;
190 void 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;
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));
207 void 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));
215 void 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));
226 void 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 return;
239 AppListFolderItem* folder = FindFolderItem(item->folder_id());
240 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
241 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
242 DCHECK_EQ(item, child_item.get());
243 FOR_EACH_OBSERVER(AppListModelObserver,
244 observers_,
245 OnAppListItemWillBeDeleted(item));
246 child_item.reset(); // Deletes item.
249 void AppListModel::NotifyExtensionPreferenceChanged() {
250 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
251 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
254 // Private methods
256 void AppListModel::OnListItemMoved(size_t from_index,
257 size_t to_index,
258 AppListItem* item) {
259 FOR_EACH_OBSERVER(AppListModelObserver,
260 observers_,
261 OnAppListItemUpdated(item));
264 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
265 const std::string& folder_id) {
266 if (folder_id.empty())
267 return NULL;
269 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
270 if (dest_folder)
271 return dest_folder;
273 DVLOG(2) << "Creating new folder: " << folder_id;
274 scoped_ptr<AppListFolderItem> new_folder(
275 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
276 new_folder->set_position(
277 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
278 AppListItem* new_folder_item =
279 AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>());
280 return static_cast<AppListFolderItem*>(new_folder_item);
283 AppListItem* AppListModel::AddItemToItemListAndNotify(
284 scoped_ptr<AppListItem> item_ptr) {
285 DCHECK(!item_ptr->IsInFolder());
286 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
287 FOR_EACH_OBSERVER(AppListModelObserver,
288 observers_,
289 OnAppListItemAdded(item));
290 return item;
293 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
294 scoped_ptr<AppListItem> item_ptr) {
295 DCHECK(!item_ptr->IsInFolder());
296 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
297 FOR_EACH_OBSERVER(AppListModelObserver,
298 observers_,
299 OnAppListItemUpdated(item));
300 return item;
303 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
304 AppListFolderItem* folder,
305 scoped_ptr<AppListItem> item_ptr) {
306 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
307 item->set_folder_id(folder->id());
308 FOR_EACH_OBSERVER(AppListModelObserver,
309 observers_,
310 OnAppListItemUpdated(item));
311 return item;
314 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
315 if (!item->IsInFolder())
316 return top_level_item_list_->RemoveItem(item->id());
318 AppListFolderItem* folder = FindFolderItem(item->folder_id());
319 return RemoveItemFromFolder(folder, item);
322 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
323 AppListFolderItem* folder,
324 AppListItem* item) {
325 std::string folder_id = folder->id();
326 DCHECK_EQ(item->folder_id(), folder_id);
327 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
328 result->set_folder_id("");
329 if (folder->item_list()->item_count() == 0) {
330 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
331 DeleteItem(folder_id);
333 return result.Pass();
336 } // namespace app_list