MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / ui / app_list / app_list_model.cc
blob0425f4c7912f9f0991a20398ea7bd63efb692719
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 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)
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 CHECK_NE(folder_id, item->folder_id());
78 DCHECK_NE(AppListFolderItem::kItemType, item->GetItemType());
79 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
80 if (!dest_folder)
81 return NULL;
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.";
91 return "";
93 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
95 if (target_item_id == source_item_id) {
96 LOG(WARNING) << "MergeItems tried to drop item onto itself ("
97 << source_item_id << " -> " << target_item_id << ").";
98 return "";
101 // Find the target item.
102 AppListItem* target_item = top_level_item_list_->FindItem(target_item_id);
103 if (!target_item) {
104 LOG(ERROR) << "MergeItems: Target no longer exists.";
105 return "";
108 AppListItem* source_item = FindItem(source_item_id);
109 if (!source_item) {
110 LOG(ERROR) << "MergeItems: Source no longer exists.";
111 return "";
114 // If the target item is a folder, just add the source item to it.
115 if (target_item->GetItemType() == AppListFolderItem::kItemType) {
116 AppListFolderItem* target_folder =
117 static_cast<AppListFolderItem*>(target_item);
118 if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
119 LOG(WARNING) << "MergeItems called with OEM folder as target";
120 return "";
122 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
123 source_item_ptr->set_position(
124 target_folder->item_list()->CreatePositionBefore(
125 syncer::StringOrdinal()));
126 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
127 return target_folder->id();
130 // Otherwise remove the source item and target item from their current
131 // location, they will become owned by the new folder.
132 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
133 CHECK(source_item_ptr);
134 // Note: This would fail if |target_item_id == source_item_id|, except we
135 // checked that they are distinct at the top of this method.
136 scoped_ptr<AppListItem> target_item_ptr =
137 top_level_item_list_->RemoveItem(target_item_id);
138 CHECK(target_item_ptr);
140 // Create a new folder in the same location as the target item.
141 std::string new_folder_id = AppListFolderItem::GenerateId();
142 DVLOG(2) << "Creating folder for merge: " << new_folder_id;
143 scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
144 new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
145 new_folder_ptr->set_position(target_item_ptr->position());
146 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
147 AddItemToItemListAndNotify(new_folder_ptr.Pass()));
149 // Add the items to the new folder.
150 target_item_ptr->set_position(
151 new_folder->item_list()->CreatePositionBefore(
152 syncer::StringOrdinal()));
153 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
154 source_item_ptr->set_position(
155 new_folder->item_list()->CreatePositionBefore(
156 syncer::StringOrdinal()));
157 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
159 return new_folder->id();
162 void AppListModel::MoveItemToFolder(AppListItem* item,
163 const std::string& folder_id) {
164 DVLOG(2) << "MoveItemToFolder: " << folder_id
165 << " <- " << item->ToDebugString();
166 if (item->folder_id() == folder_id)
167 return;
168 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
169 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
170 if (dest_folder) {
171 CHECK(!item->IsInFolder());
172 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
173 } else {
174 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
178 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
179 const std::string& folder_id,
180 syncer::StringOrdinal position) {
181 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
182 << "[" << position.ToDebugString() << "]"
183 << " <- " << item->ToDebugString();
184 if (item->folder_id() == folder_id)
185 return false;
186 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
187 if (src_folder &&
188 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
189 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
190 return false;
192 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
193 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
194 if (dest_folder) {
195 item_ptr->set_position(
196 dest_folder->item_list()->CreatePositionBefore(position));
197 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
198 } else {
199 item_ptr->set_position(
200 top_level_item_list_->CreatePositionBefore(position));
201 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
203 return true;
206 void AppListModel::SetItemPosition(AppListItem* item,
207 const syncer::StringOrdinal& new_position) {
208 if (!item->IsInFolder()) {
209 top_level_item_list_->SetItemPosition(item, new_position);
210 // Note: this will trigger OnListItemMoved which will signal observers.
211 // (This is done this way because some View code still moves items within
212 // the item list directly).
213 return;
215 AppListFolderItem* folder = FindFolderItem(item->folder_id());
216 DCHECK(folder);
217 folder->item_list()->SetItemPosition(item, new_position);
218 FOR_EACH_OBSERVER(AppListModelObserver,
219 observers_,
220 OnAppListItemUpdated(item));
223 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
224 item->SetName(name);
225 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
226 FOR_EACH_OBSERVER(AppListModelObserver,
227 observers_,
228 OnAppListItemUpdated(item));
231 void AppListModel::SetItemNameAndShortName(AppListItem* item,
232 const std::string& name,
233 const std::string& short_name) {
234 item->SetNameAndShortName(name, short_name);
235 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
236 << item->ToDebugString();
237 FOR_EACH_OBSERVER(AppListModelObserver,
238 observers_,
239 OnAppListItemUpdated(item));
242 void AppListModel::DeleteItem(const std::string& id) {
243 AppListItem* item = FindItem(id);
244 if (!item)
245 return;
246 if (!item->IsInFolder()) {
247 DCHECK_EQ(0u, item->ChildItemCount())
248 << "Invalid call to DeleteItem for item with children: " << id;
249 FOR_EACH_OBSERVER(AppListModelObserver,
250 observers_,
251 OnAppListItemWillBeDeleted(item));
252 top_level_item_list_->DeleteItem(id);
253 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
254 return;
256 AppListFolderItem* folder = FindFolderItem(item->folder_id());
257 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
258 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
259 DCHECK_EQ(item, child_item.get());
260 FOR_EACH_OBSERVER(AppListModelObserver,
261 observers_,
262 OnAppListItemWillBeDeleted(item));
263 child_item.reset(); // Deletes item.
264 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
267 void AppListModel::NotifyExtensionPreferenceChanged() {
268 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
269 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
272 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
273 folders_enabled_ = folders_enabled;
274 if (folders_enabled)
275 return;
276 // Remove child items from folders.
277 std::vector<std::string> folder_ids;
278 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
279 AppListItem* item = top_level_item_list_->item_at(i);
280 if (item->GetItemType() != AppListFolderItem::kItemType)
281 continue;
282 AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
283 if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
284 continue; // Do not remove OEM folders.
285 while (folder->item_list()->item_count()) {
286 scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
287 child->set_folder_id("");
288 AddItemToItemListAndNotifyUpdate(child.Pass());
290 folder_ids.push_back(folder->id());
292 // Delete folders.
293 for (size_t i = 0; i < folder_ids.size(); ++i)
294 DeleteItem(folder_ids[i]);
297 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
298 SearchResults* results,
299 SearchResult::DisplayType display_type,
300 size_t max_results) {
301 std::vector<SearchResult*> matches;
302 for (size_t i = 0; i < results->item_count(); ++i) {
303 SearchResult* item = results->GetItemAt(i);
304 if (item->display_type() == display_type) {
305 matches.push_back(item);
306 if (matches.size() == max_results)
307 break;
310 return matches;
313 // Private methods
315 void AppListModel::OnListItemMoved(size_t from_index,
316 size_t to_index,
317 AppListItem* item) {
318 FOR_EACH_OBSERVER(AppListModelObserver,
319 observers_,
320 OnAppListItemUpdated(item));
323 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
324 const std::string& folder_id) {
325 if (folder_id.empty())
326 return NULL;
328 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
329 if (dest_folder)
330 return dest_folder;
332 if (!folders_enabled()) {
333 LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
334 return NULL;
337 DVLOG(2) << "Creating new folder: " << folder_id;
338 scoped_ptr<AppListFolderItem> new_folder(
339 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
340 new_folder->set_position(
341 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
342 AppListItem* new_folder_item = AddItemToItemListAndNotify(new_folder.Pass());
343 return static_cast<AppListFolderItem*>(new_folder_item);
346 AppListItem* AppListModel::AddItemToItemListAndNotify(
347 scoped_ptr<AppListItem> item_ptr) {
348 DCHECK(!item_ptr->IsInFolder());
349 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
350 FOR_EACH_OBSERVER(AppListModelObserver,
351 observers_,
352 OnAppListItemAdded(item));
353 return item;
356 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
357 scoped_ptr<AppListItem> item_ptr) {
358 DCHECK(!item_ptr->IsInFolder());
359 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
360 FOR_EACH_OBSERVER(AppListModelObserver,
361 observers_,
362 OnAppListItemUpdated(item));
363 return item;
366 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
367 AppListFolderItem* folder,
368 scoped_ptr<AppListItem> item_ptr) {
369 CHECK_NE(folder->id(), item_ptr->folder_id());
370 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
371 item->set_folder_id(folder->id());
372 FOR_EACH_OBSERVER(AppListModelObserver,
373 observers_,
374 OnAppListItemUpdated(item));
375 return item;
378 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
379 if (!item->IsInFolder())
380 return top_level_item_list_->RemoveItem(item->id());
382 AppListFolderItem* folder = FindFolderItem(item->folder_id());
383 return RemoveItemFromFolder(folder, item);
386 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
387 AppListFolderItem* folder,
388 AppListItem* item) {
389 std::string folder_id = folder->id();
390 CHECK_EQ(item->folder_id(), folder_id);
391 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
392 result->set_folder_id("");
393 if (folder->item_list()->item_count() == 0) {
394 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
395 DeleteItem(folder_id);
397 return result.Pass();
400 } // namespace app_list