Drive: Add BatchableRequest subclass.
[chromium-blink-merge.git] / ui / app_list / app_list_model.cc
blob48394478d48db0605cafddc3bcadcc3e82c3acdf
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"
14 namespace app_list {
16 AppListModel::AppListModel()
17 : top_level_item_list_(new AppListItemList),
18 search_box_(new SearchBoxModel),
19 results_(new SearchResults),
20 status_(STATUS_NORMAL),
21 state_(INVALID_STATE),
22 folders_enabled_(false),
23 custom_launcher_page_enabled_(true),
24 search_engine_is_google_(false) {
25 top_level_item_list_->AddObserver(this);
28 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
30 void AppListModel::AddObserver(AppListModelObserver* observer) {
31 observers_.AddObserver(observer);
34 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
35 observers_.RemoveObserver(observer);
38 void AppListModel::SetStatus(Status status) {
39 if (status_ == status)
40 return;
42 status_ = status;
43 FOR_EACH_OBSERVER(AppListModelObserver,
44 observers_,
45 OnAppListModelStatusChanged());
48 void AppListModel::SetState(State state) {
49 if (state_ == state)
50 return;
52 State old_state = state_;
54 state_ = state;
56 FOR_EACH_OBSERVER(AppListModelObserver,
57 observers_,
58 OnAppListModelStateChanged(old_state, state_));
61 AppListItem* AppListModel::FindItem(const std::string& id) {
62 AppListItem* item = top_level_item_list_->FindItem(id);
63 if (item)
64 return item;
65 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
66 AppListItem* child_item =
67 top_level_item_list_->item_at(i)->FindChildItem(id);
68 if (child_item)
69 return child_item;
71 return NULL;
74 AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) {
75 AppListItem* item = top_level_item_list_->FindItem(id);
76 if (item && item->GetItemType() == AppListFolderItem::kItemType)
77 return static_cast<AppListFolderItem*>(item);
78 DCHECK(!item);
79 return NULL;
82 AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) {
83 DCHECK(!item->IsInFolder());
84 DCHECK(!top_level_item_list()->FindItem(item->id()));
85 return AddItemToItemListAndNotify(item.Pass());
88 AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item,
89 const std::string& folder_id) {
90 if (folder_id.empty())
91 return AddItem(item.Pass());
92 DVLOG(2) << "AddItemToFolder: " << item->id() << ": " << folder_id;
93 CHECK_NE(folder_id, item->folder_id());
94 DCHECK_NE(AppListFolderItem::kItemType, item->GetItemType());
95 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
96 if (!dest_folder)
97 return NULL;
98 DCHECK(!dest_folder->item_list()->FindItem(item->id()))
99 << "Already in folder: " << dest_folder->id();
100 return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
103 const std::string AppListModel::MergeItems(const std::string& target_item_id,
104 const std::string& source_item_id) {
105 if (!folders_enabled()) {
106 LOG(ERROR) << "MergeItems called with folders disabled.";
107 return "";
109 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
111 if (target_item_id == source_item_id) {
112 LOG(WARNING) << "MergeItems tried to drop item onto itself ("
113 << source_item_id << " -> " << target_item_id << ").";
114 return "";
117 // Find the target item.
118 AppListItem* target_item = top_level_item_list_->FindItem(target_item_id);
119 if (!target_item) {
120 LOG(ERROR) << "MergeItems: Target no longer exists.";
121 return "";
124 AppListItem* source_item = FindItem(source_item_id);
125 if (!source_item) {
126 LOG(ERROR) << "MergeItems: Source no longer exists.";
127 return "";
130 // If the target item is a folder, just add the source item to it.
131 if (target_item->GetItemType() == AppListFolderItem::kItemType) {
132 AppListFolderItem* target_folder =
133 static_cast<AppListFolderItem*>(target_item);
134 if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
135 LOG(WARNING) << "MergeItems called with OEM folder as target";
136 return "";
138 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
139 source_item_ptr->set_position(
140 target_folder->item_list()->CreatePositionBefore(
141 syncer::StringOrdinal()));
142 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
143 return target_folder->id();
146 // Otherwise remove the source item and target item from their current
147 // location, they will become owned by the new folder.
148 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
149 CHECK(source_item_ptr);
150 // Note: This would fail if |target_item_id == source_item_id|, except we
151 // checked that they are distinct at the top of this method.
152 scoped_ptr<AppListItem> target_item_ptr =
153 top_level_item_list_->RemoveItem(target_item_id);
154 CHECK(target_item_ptr);
156 // Create a new folder in the same location as the target item.
157 std::string new_folder_id = AppListFolderItem::GenerateId();
158 DVLOG(2) << "Creating folder for merge: " << new_folder_id;
159 scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
160 new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
161 new_folder_ptr->set_position(target_item_ptr->position());
162 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
163 AddItemToItemListAndNotify(new_folder_ptr.Pass()));
165 // Add the items to the new folder.
166 target_item_ptr->set_position(
167 new_folder->item_list()->CreatePositionBefore(
168 syncer::StringOrdinal()));
169 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
170 source_item_ptr->set_position(
171 new_folder->item_list()->CreatePositionBefore(
172 syncer::StringOrdinal()));
173 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
175 return new_folder->id();
178 void AppListModel::MoveItemToFolder(AppListItem* item,
179 const std::string& folder_id) {
180 DVLOG(2) << "MoveItemToFolder: " << folder_id
181 << " <- " << item->ToDebugString();
182 if (item->folder_id() == folder_id)
183 return;
184 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
185 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
186 if (dest_folder) {
187 CHECK(!item->IsInFolder());
188 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
189 } else {
190 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
194 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
195 const std::string& folder_id,
196 syncer::StringOrdinal position) {
197 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
198 << "[" << position.ToDebugString() << "]"
199 << " <- " << item->ToDebugString();
200 if (item->folder_id() == folder_id)
201 return false;
202 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
203 if (src_folder &&
204 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
205 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
206 return false;
208 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
209 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
210 if (dest_folder) {
211 item_ptr->set_position(
212 dest_folder->item_list()->CreatePositionBefore(position));
213 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
214 } else {
215 item_ptr->set_position(
216 top_level_item_list_->CreatePositionBefore(position));
217 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
219 return true;
222 void AppListModel::SetItemPosition(AppListItem* item,
223 const syncer::StringOrdinal& new_position) {
224 if (!item->IsInFolder()) {
225 top_level_item_list_->SetItemPosition(item, new_position);
226 // Note: this will trigger OnListItemMoved which will signal observers.
227 // (This is done this way because some View code still moves items within
228 // the item list directly).
229 return;
231 AppListFolderItem* folder = FindFolderItem(item->folder_id());
232 DCHECK(folder);
233 folder->item_list()->SetItemPosition(item, new_position);
234 FOR_EACH_OBSERVER(AppListModelObserver,
235 observers_,
236 OnAppListItemUpdated(item));
239 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
240 item->SetName(name);
241 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
242 FOR_EACH_OBSERVER(AppListModelObserver,
243 observers_,
244 OnAppListItemUpdated(item));
247 void AppListModel::SetItemNameAndShortName(AppListItem* item,
248 const std::string& name,
249 const std::string& short_name) {
250 item->SetNameAndShortName(name, short_name);
251 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
252 << item->ToDebugString();
253 FOR_EACH_OBSERVER(AppListModelObserver,
254 observers_,
255 OnAppListItemUpdated(item));
258 void AppListModel::DeleteItem(const std::string& id) {
259 AppListItem* item = FindItem(id);
260 if (!item)
261 return;
262 if (!item->IsInFolder()) {
263 DCHECK_EQ(0u, item->ChildItemCount())
264 << "Invalid call to DeleteItem for item with children: " << id;
265 FOR_EACH_OBSERVER(AppListModelObserver,
266 observers_,
267 OnAppListItemWillBeDeleted(item));
268 top_level_item_list_->DeleteItem(id);
269 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
270 return;
272 AppListFolderItem* folder = FindFolderItem(item->folder_id());
273 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
274 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
275 DCHECK_EQ(item, child_item.get());
276 FOR_EACH_OBSERVER(AppListModelObserver,
277 observers_,
278 OnAppListItemWillBeDeleted(item));
279 child_item.reset(); // Deletes item.
280 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
283 void AppListModel::DeleteUninstalledItem(const std::string& id) {
284 AppListItem* item = FindItem(id);
285 if (!item)
286 return;
287 const std::string folder_id = item->folder_id();
288 DeleteItem(id);
290 // crbug.com/368111: Upon uninstall of 2nd-to-last folder item, reparent last
291 // item to top; this will remove the folder.
292 AppListFolderItem* folder = FindFolderItem(folder_id);
293 if (folder && folder->ChildItemCount() == 1u) {
294 AppListItem* last_item = folder->item_list()->item_at(0);
295 MoveItemToFolderAt(last_item, "", folder->position());
299 void AppListModel::NotifyExtensionPreferenceChanged() {
300 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
301 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
304 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
305 folders_enabled_ = folders_enabled;
306 if (folders_enabled)
307 return;
308 // Remove child items from folders.
309 std::vector<std::string> folder_ids;
310 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
311 AppListItem* item = top_level_item_list_->item_at(i);
312 if (item->GetItemType() != AppListFolderItem::kItemType)
313 continue;
314 AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
315 if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
316 continue; // Do not remove OEM folders.
317 while (folder->item_list()->item_count()) {
318 scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
319 child->set_folder_id("");
320 AddItemToItemListAndNotifyUpdate(child.Pass());
322 folder_ids.push_back(folder->id());
324 // Delete folders.
325 for (size_t i = 0; i < folder_ids.size(); ++i)
326 DeleteItem(folder_ids[i]);
329 void AppListModel::SetCustomLauncherPageEnabled(bool enabled) {
330 custom_launcher_page_enabled_ = enabled;
331 FOR_EACH_OBSERVER(AppListModelObserver, observers_,
332 OnCustomLauncherPageEnabledStateChanged(enabled));
335 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
336 SearchResults* results,
337 SearchResult::DisplayType display_type,
338 size_t max_results) {
339 std::vector<SearchResult*> matches;
340 for (size_t i = 0; i < results->item_count(); ++i) {
341 SearchResult* item = results->GetItemAt(i);
342 if (item->display_type() == display_type) {
343 matches.push_back(item);
344 if (matches.size() == max_results)
345 break;
348 return matches;
351 void AppListModel::PushCustomLauncherPageSubpage() {
352 custom_launcher_page_subpage_depth_++;
355 bool AppListModel::PopCustomLauncherPageSubpage() {
356 if (custom_launcher_page_subpage_depth_ == 0)
357 return false;
359 --custom_launcher_page_subpage_depth_;
360 return true;
363 void AppListModel::ClearCustomLauncherPageSubpages() {
364 custom_launcher_page_subpage_depth_ = 0;
367 void AppListModel::SetSearchEngineIsGoogle(bool is_google) {
368 search_engine_is_google_ = is_google;
369 FOR_EACH_OBSERVER(AppListModelObserver, observers_,
370 OnSearchEngineIsGoogleChanged(is_google));
373 // Private methods
375 void AppListModel::OnListItemMoved(size_t from_index,
376 size_t to_index,
377 AppListItem* item) {
378 FOR_EACH_OBSERVER(AppListModelObserver,
379 observers_,
380 OnAppListItemUpdated(item));
383 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
384 const std::string& folder_id) {
385 if (folder_id.empty())
386 return NULL;
388 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
389 if (dest_folder)
390 return dest_folder;
392 if (!folders_enabled()) {
393 LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
394 return NULL;
397 DVLOG(2) << "Creating new folder: " << folder_id;
398 scoped_ptr<AppListFolderItem> new_folder(
399 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
400 new_folder->set_position(
401 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
402 AppListItem* new_folder_item = AddItemToItemListAndNotify(new_folder.Pass());
403 return static_cast<AppListFolderItem*>(new_folder_item);
406 AppListItem* AppListModel::AddItemToItemListAndNotify(
407 scoped_ptr<AppListItem> item_ptr) {
408 DCHECK(!item_ptr->IsInFolder());
409 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
410 FOR_EACH_OBSERVER(AppListModelObserver,
411 observers_,
412 OnAppListItemAdded(item));
413 return item;
416 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
417 scoped_ptr<AppListItem> item_ptr) {
418 DCHECK(!item_ptr->IsInFolder());
419 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
420 FOR_EACH_OBSERVER(AppListModelObserver,
421 observers_,
422 OnAppListItemUpdated(item));
423 return item;
426 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
427 AppListFolderItem* folder,
428 scoped_ptr<AppListItem> item_ptr) {
429 CHECK_NE(folder->id(), item_ptr->folder_id());
430 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
431 item->set_folder_id(folder->id());
432 FOR_EACH_OBSERVER(AppListModelObserver,
433 observers_,
434 OnAppListItemUpdated(item));
435 return item;
438 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
439 if (!item->IsInFolder())
440 return top_level_item_list_->RemoveItem(item->id());
442 AppListFolderItem* folder = FindFolderItem(item->folder_id());
443 return RemoveItemFromFolder(folder, item);
446 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
447 AppListFolderItem* folder,
448 AppListItem* item) {
449 std::string folder_id = folder->id();
450 CHECK_EQ(item->folder_id(), folder_id);
451 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
452 result->set_folder_id("");
453 if (folder->item_list()->item_count() == 0) {
454 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
455 DeleteItem(folder_id);
457 return result.Pass();
460 } // namespace app_list