Add testing/scripts/OWNERS
[chromium-blink-merge.git] / ui / app_list / app_list_model.cc
blobc2c8237540b37d96d9b4ad0437af9bf07d28e9f8
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 top_level_item_list_->AddObserver(this);
26 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
28 void AppListModel::AddObserver(AppListModelObserver* observer) {
29 observers_.AddObserver(observer);
32 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
33 observers_.RemoveObserver(observer);
36 void AppListModel::SetStatus(Status status) {
37 if (status_ == status)
38 return;
40 status_ = status;
41 FOR_EACH_OBSERVER(AppListModelObserver,
42 observers_,
43 OnAppListModelStatusChanged());
46 void AppListModel::SetState(State state) {
47 if (state_ == state)
48 return;
50 State old_state = state_;
52 state_ = state;
54 FOR_EACH_OBSERVER(AppListModelObserver,
55 observers_,
56 OnAppListModelStateChanged(old_state, state_));
59 AppListItem* AppListModel::FindItem(const std::string& id) {
60 AppListItem* item = top_level_item_list_->FindItem(id);
61 if (item)
62 return item;
63 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
64 AppListItem* child_item =
65 top_level_item_list_->item_at(i)->FindChildItem(id);
66 if (child_item)
67 return child_item;
69 return NULL;
72 AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) {
73 AppListItem* item = top_level_item_list_->FindItem(id);
74 if (item && item->GetItemType() == AppListFolderItem::kItemType)
75 return static_cast<AppListFolderItem*>(item);
76 DCHECK(!item);
77 return NULL;
80 AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) {
81 DCHECK(!item->IsInFolder());
82 DCHECK(!top_level_item_list()->FindItem(item->id()));
83 return AddItemToItemListAndNotify(item.Pass());
86 AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item,
87 const std::string& folder_id) {
88 if (folder_id.empty())
89 return AddItem(item.Pass());
90 DVLOG(2) << "AddItemToFolder: " << item->id() << ": " << folder_id;
91 CHECK_NE(folder_id, item->folder_id());
92 DCHECK_NE(AppListFolderItem::kItemType, item->GetItemType());
93 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
94 if (!dest_folder)
95 return NULL;
96 DCHECK(!dest_folder->item_list()->FindItem(item->id()))
97 << "Already in folder: " << dest_folder->id();
98 return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
101 const std::string AppListModel::MergeItems(const std::string& target_item_id,
102 const std::string& source_item_id) {
103 if (!folders_enabled()) {
104 LOG(ERROR) << "MergeItems called with folders disabled.";
105 return "";
107 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
109 if (target_item_id == source_item_id) {
110 LOG(WARNING) << "MergeItems tried to drop item onto itself ("
111 << source_item_id << " -> " << target_item_id << ").";
112 return "";
115 // Find the target item.
116 AppListItem* target_item = top_level_item_list_->FindItem(target_item_id);
117 if (!target_item) {
118 LOG(ERROR) << "MergeItems: Target no longer exists.";
119 return "";
122 AppListItem* source_item = FindItem(source_item_id);
123 if (!source_item) {
124 LOG(ERROR) << "MergeItems: Source no longer exists.";
125 return "";
128 // If the target item is a folder, just add the source item to it.
129 if (target_item->GetItemType() == AppListFolderItem::kItemType) {
130 AppListFolderItem* target_folder =
131 static_cast<AppListFolderItem*>(target_item);
132 if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
133 LOG(WARNING) << "MergeItems called with OEM folder as target";
134 return "";
136 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
137 source_item_ptr->set_position(
138 target_folder->item_list()->CreatePositionBefore(
139 syncer::StringOrdinal()));
140 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
141 return target_folder->id();
144 // Otherwise remove the source item and target item from their current
145 // location, they will become owned by the new folder.
146 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
147 CHECK(source_item_ptr);
148 // Note: This would fail if |target_item_id == source_item_id|, except we
149 // checked that they are distinct at the top of this method.
150 scoped_ptr<AppListItem> target_item_ptr =
151 top_level_item_list_->RemoveItem(target_item_id);
152 CHECK(target_item_ptr);
154 // Create a new folder in the same location as the target item.
155 std::string new_folder_id = AppListFolderItem::GenerateId();
156 DVLOG(2) << "Creating folder for merge: " << new_folder_id;
157 scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
158 new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
159 new_folder_ptr->set_position(target_item_ptr->position());
160 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
161 AddItemToItemListAndNotify(new_folder_ptr.Pass()));
163 // Add the items to the new folder.
164 target_item_ptr->set_position(
165 new_folder->item_list()->CreatePositionBefore(
166 syncer::StringOrdinal()));
167 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
168 source_item_ptr->set_position(
169 new_folder->item_list()->CreatePositionBefore(
170 syncer::StringOrdinal()));
171 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
173 return new_folder->id();
176 void AppListModel::MoveItemToFolder(AppListItem* item,
177 const std::string& folder_id) {
178 DVLOG(2) << "MoveItemToFolder: " << folder_id
179 << " <- " << item->ToDebugString();
180 if (item->folder_id() == folder_id)
181 return;
182 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
183 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
184 if (dest_folder) {
185 CHECK(!item->IsInFolder());
186 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
187 } else {
188 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
192 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
193 const std::string& folder_id,
194 syncer::StringOrdinal position) {
195 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
196 << "[" << position.ToDebugString() << "]"
197 << " <- " << item->ToDebugString();
198 if (item->folder_id() == folder_id)
199 return false;
200 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
201 if (src_folder &&
202 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
203 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
204 return false;
206 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
207 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
208 if (dest_folder) {
209 item_ptr->set_position(
210 dest_folder->item_list()->CreatePositionBefore(position));
211 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
212 } else {
213 item_ptr->set_position(
214 top_level_item_list_->CreatePositionBefore(position));
215 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
217 return true;
220 void AppListModel::SetItemPosition(AppListItem* item,
221 const syncer::StringOrdinal& new_position) {
222 if (!item->IsInFolder()) {
223 top_level_item_list_->SetItemPosition(item, new_position);
224 // Note: this will trigger OnListItemMoved which will signal observers.
225 // (This is done this way because some View code still moves items within
226 // the item list directly).
227 return;
229 AppListFolderItem* folder = FindFolderItem(item->folder_id());
230 DCHECK(folder);
231 folder->item_list()->SetItemPosition(item, new_position);
232 FOR_EACH_OBSERVER(AppListModelObserver,
233 observers_,
234 OnAppListItemUpdated(item));
237 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
238 item->SetName(name);
239 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
240 FOR_EACH_OBSERVER(AppListModelObserver,
241 observers_,
242 OnAppListItemUpdated(item));
245 void AppListModel::SetItemNameAndShortName(AppListItem* item,
246 const std::string& name,
247 const std::string& short_name) {
248 item->SetNameAndShortName(name, short_name);
249 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
250 << item->ToDebugString();
251 FOR_EACH_OBSERVER(AppListModelObserver,
252 observers_,
253 OnAppListItemUpdated(item));
256 void AppListModel::DeleteItem(const std::string& id) {
257 AppListItem* item = FindItem(id);
258 if (!item)
259 return;
260 if (!item->IsInFolder()) {
261 DCHECK_EQ(0u, item->ChildItemCount())
262 << "Invalid call to DeleteItem for item with children: " << id;
263 FOR_EACH_OBSERVER(AppListModelObserver,
264 observers_,
265 OnAppListItemWillBeDeleted(item));
266 top_level_item_list_->DeleteItem(id);
267 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
268 return;
270 AppListFolderItem* folder = FindFolderItem(item->folder_id());
271 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
272 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
273 DCHECK_EQ(item, child_item.get());
274 FOR_EACH_OBSERVER(AppListModelObserver,
275 observers_,
276 OnAppListItemWillBeDeleted(item));
277 child_item.reset(); // Deletes item.
278 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
281 void AppListModel::NotifyExtensionPreferenceChanged() {
282 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
283 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
286 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
287 folders_enabled_ = folders_enabled;
288 if (folders_enabled)
289 return;
290 // Remove child items from folders.
291 std::vector<std::string> folder_ids;
292 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
293 AppListItem* item = top_level_item_list_->item_at(i);
294 if (item->GetItemType() != AppListFolderItem::kItemType)
295 continue;
296 AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
297 if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
298 continue; // Do not remove OEM folders.
299 while (folder->item_list()->item_count()) {
300 scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
301 child->set_folder_id("");
302 AddItemToItemListAndNotifyUpdate(child.Pass());
304 folder_ids.push_back(folder->id());
306 // Delete folders.
307 for (size_t i = 0; i < folder_ids.size(); ++i)
308 DeleteItem(folder_ids[i]);
311 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
312 SearchResults* results,
313 SearchResult::DisplayType display_type,
314 size_t max_results) {
315 std::vector<SearchResult*> matches;
316 for (size_t i = 0; i < results->item_count(); ++i) {
317 SearchResult* item = results->GetItemAt(i);
318 if (item->display_type() == display_type) {
319 matches.push_back(item);
320 if (matches.size() == max_results)
321 break;
324 return matches;
327 // Private methods
329 void AppListModel::OnListItemMoved(size_t from_index,
330 size_t to_index,
331 AppListItem* item) {
332 FOR_EACH_OBSERVER(AppListModelObserver,
333 observers_,
334 OnAppListItemUpdated(item));
337 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
338 const std::string& folder_id) {
339 if (folder_id.empty())
340 return NULL;
342 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
343 if (dest_folder)
344 return dest_folder;
346 if (!folders_enabled()) {
347 LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
348 return NULL;
351 DVLOG(2) << "Creating new folder: " << folder_id;
352 scoped_ptr<AppListFolderItem> new_folder(
353 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
354 new_folder->set_position(
355 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
356 AppListItem* new_folder_item = AddItemToItemListAndNotify(new_folder.Pass());
357 return static_cast<AppListFolderItem*>(new_folder_item);
360 AppListItem* AppListModel::AddItemToItemListAndNotify(
361 scoped_ptr<AppListItem> item_ptr) {
362 DCHECK(!item_ptr->IsInFolder());
363 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
364 FOR_EACH_OBSERVER(AppListModelObserver,
365 observers_,
366 OnAppListItemAdded(item));
367 return item;
370 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
371 scoped_ptr<AppListItem> item_ptr) {
372 DCHECK(!item_ptr->IsInFolder());
373 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
374 FOR_EACH_OBSERVER(AppListModelObserver,
375 observers_,
376 OnAppListItemUpdated(item));
377 return item;
380 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
381 AppListFolderItem* folder,
382 scoped_ptr<AppListItem> item_ptr) {
383 CHECK_NE(folder->id(), item_ptr->folder_id());
384 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
385 item->set_folder_id(folder->id());
386 FOR_EACH_OBSERVER(AppListModelObserver,
387 observers_,
388 OnAppListItemUpdated(item));
389 return item;
392 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
393 if (!item->IsInFolder())
394 return top_level_item_list_->RemoveItem(item->id());
396 AppListFolderItem* folder = FindFolderItem(item->folder_id());
397 return RemoveItemFromFolder(folder, item);
400 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
401 AppListFolderItem* folder,
402 AppListItem* item) {
403 std::string folder_id = folder->id();
404 CHECK_EQ(item->folder_id(), folder_id);
405 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
406 result->set_folder_id("");
407 if (folder->item_list()->item_count() == 0) {
408 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
409 DeleteItem(folder_id);
411 return result.Pass();
414 } // namespace app_list