Conform selector used in focus manager of history to <cr-menu> element.
[chromium-blink-merge.git] / ui / app_list / app_list_model.cc
blob491087ec18c90447625f11ce35a4d7addc46d4e6
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 top_level_item_list_->AddObserver(this);
27 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
29 void AppListModel::AddObserver(AppListModelObserver* observer) {
30 observers_.AddObserver(observer);
33 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
34 observers_.RemoveObserver(observer);
37 void AppListModel::SetStatus(Status status) {
38 if (status_ == status)
39 return;
41 status_ = status;
42 FOR_EACH_OBSERVER(AppListModelObserver,
43 observers_,
44 OnAppListModelStatusChanged());
47 void AppListModel::SetState(State state) {
48 if (state_ == state)
49 return;
51 State old_state = state_;
53 state_ = state;
55 FOR_EACH_OBSERVER(AppListModelObserver,
56 observers_,
57 OnAppListModelStateChanged(old_state, state_));
60 AppListItem* AppListModel::FindItem(const std::string& id) {
61 AppListItem* item = top_level_item_list_->FindItem(id);
62 if (item)
63 return item;
64 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
65 AppListItem* child_item =
66 top_level_item_list_->item_at(i)->FindChildItem(id);
67 if (child_item)
68 return child_item;
70 return NULL;
73 AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) {
74 AppListItem* item = top_level_item_list_->FindItem(id);
75 if (item && item->GetItemType() == AppListFolderItem::kItemType)
76 return static_cast<AppListFolderItem*>(item);
77 DCHECK(!item);
78 return NULL;
81 AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) {
82 DCHECK(!item->IsInFolder());
83 DCHECK(!top_level_item_list()->FindItem(item->id()));
84 return AddItemToItemListAndNotify(item.Pass());
87 AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item,
88 const std::string& folder_id) {
89 if (folder_id.empty())
90 return AddItem(item.Pass());
91 DVLOG(2) << "AddItemToFolder: " << item->id() << ": " << folder_id;
92 CHECK_NE(folder_id, item->folder_id());
93 DCHECK_NE(AppListFolderItem::kItemType, item->GetItemType());
94 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
95 if (!dest_folder)
96 return NULL;
97 DCHECK(!dest_folder->item_list()->FindItem(item->id()))
98 << "Already in folder: " << dest_folder->id();
99 return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
102 const std::string AppListModel::MergeItems(const std::string& target_item_id,
103 const std::string& source_item_id) {
104 if (!folders_enabled()) {
105 LOG(ERROR) << "MergeItems called with folders disabled.";
106 return "";
108 DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
110 if (target_item_id == source_item_id) {
111 LOG(WARNING) << "MergeItems tried to drop item onto itself ("
112 << source_item_id << " -> " << target_item_id << ").";
113 return "";
116 // Find the target item.
117 AppListItem* target_item = top_level_item_list_->FindItem(target_item_id);
118 if (!target_item) {
119 LOG(ERROR) << "MergeItems: Target no longer exists.";
120 return "";
123 AppListItem* source_item = FindItem(source_item_id);
124 if (!source_item) {
125 LOG(ERROR) << "MergeItems: Source no longer exists.";
126 return "";
129 // If the target item is a folder, just add the source item to it.
130 if (target_item->GetItemType() == AppListFolderItem::kItemType) {
131 AppListFolderItem* target_folder =
132 static_cast<AppListFolderItem*>(target_item);
133 if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
134 LOG(WARNING) << "MergeItems called with OEM folder as target";
135 return "";
137 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
138 source_item_ptr->set_position(
139 target_folder->item_list()->CreatePositionBefore(
140 syncer::StringOrdinal()));
141 AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
142 return target_folder->id();
145 // Otherwise remove the source item and target item from their current
146 // location, they will become owned by the new folder.
147 scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
148 CHECK(source_item_ptr);
149 // Note: This would fail if |target_item_id == source_item_id|, except we
150 // checked that they are distinct at the top of this method.
151 scoped_ptr<AppListItem> target_item_ptr =
152 top_level_item_list_->RemoveItem(target_item_id);
153 CHECK(target_item_ptr);
155 // Create a new folder in the same location as the target item.
156 std::string new_folder_id = AppListFolderItem::GenerateId();
157 DVLOG(2) << "Creating folder for merge: " << new_folder_id;
158 scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
159 new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
160 new_folder_ptr->set_position(target_item_ptr->position());
161 AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
162 AddItemToItemListAndNotify(new_folder_ptr.Pass()));
164 // Add the items to the new folder.
165 target_item_ptr->set_position(
166 new_folder->item_list()->CreatePositionBefore(
167 syncer::StringOrdinal()));
168 AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
169 source_item_ptr->set_position(
170 new_folder->item_list()->CreatePositionBefore(
171 syncer::StringOrdinal()));
172 AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
174 return new_folder->id();
177 void AppListModel::MoveItemToFolder(AppListItem* item,
178 const std::string& folder_id) {
179 DVLOG(2) << "MoveItemToFolder: " << folder_id
180 << " <- " << item->ToDebugString();
181 if (item->folder_id() == folder_id)
182 return;
183 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
184 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
185 if (dest_folder) {
186 CHECK(!item->IsInFolder());
187 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
188 } else {
189 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
193 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
194 const std::string& folder_id,
195 syncer::StringOrdinal position) {
196 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
197 << "[" << position.ToDebugString() << "]"
198 << " <- " << item->ToDebugString();
199 if (item->folder_id() == folder_id)
200 return false;
201 AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
202 if (src_folder &&
203 src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
204 LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
205 return false;
207 AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
208 scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
209 if (dest_folder) {
210 item_ptr->set_position(
211 dest_folder->item_list()->CreatePositionBefore(position));
212 AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
213 } else {
214 item_ptr->set_position(
215 top_level_item_list_->CreatePositionBefore(position));
216 AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
218 return true;
221 void AppListModel::SetItemPosition(AppListItem* item,
222 const syncer::StringOrdinal& new_position) {
223 if (!item->IsInFolder()) {
224 top_level_item_list_->SetItemPosition(item, new_position);
225 // Note: this will trigger OnListItemMoved which will signal observers.
226 // (This is done this way because some View code still moves items within
227 // the item list directly).
228 return;
230 AppListFolderItem* folder = FindFolderItem(item->folder_id());
231 DCHECK(folder);
232 folder->item_list()->SetItemPosition(item, new_position);
233 FOR_EACH_OBSERVER(AppListModelObserver,
234 observers_,
235 OnAppListItemUpdated(item));
238 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
239 item->SetName(name);
240 DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
241 FOR_EACH_OBSERVER(AppListModelObserver,
242 observers_,
243 OnAppListItemUpdated(item));
246 void AppListModel::SetItemNameAndShortName(AppListItem* item,
247 const std::string& name,
248 const std::string& short_name) {
249 item->SetNameAndShortName(name, short_name);
250 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
251 << item->ToDebugString();
252 FOR_EACH_OBSERVER(AppListModelObserver,
253 observers_,
254 OnAppListItemUpdated(item));
257 void AppListModel::DeleteItem(const std::string& id) {
258 AppListItem* item = FindItem(id);
259 if (!item)
260 return;
261 if (!item->IsInFolder()) {
262 DCHECK_EQ(0u, item->ChildItemCount())
263 << "Invalid call to DeleteItem for item with children: " << id;
264 FOR_EACH_OBSERVER(AppListModelObserver,
265 observers_,
266 OnAppListItemWillBeDeleted(item));
267 top_level_item_list_->DeleteItem(id);
268 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
269 return;
271 AppListFolderItem* folder = FindFolderItem(item->folder_id());
272 DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
273 scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
274 DCHECK_EQ(item, child_item.get());
275 FOR_EACH_OBSERVER(AppListModelObserver,
276 observers_,
277 OnAppListItemWillBeDeleted(item));
278 child_item.reset(); // Deletes item.
279 FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
282 void AppListModel::NotifyExtensionPreferenceChanged() {
283 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
284 top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
287 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
288 folders_enabled_ = folders_enabled;
289 if (folders_enabled)
290 return;
291 // Remove child items from folders.
292 std::vector<std::string> folder_ids;
293 for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
294 AppListItem* item = top_level_item_list_->item_at(i);
295 if (item->GetItemType() != AppListFolderItem::kItemType)
296 continue;
297 AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
298 if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
299 continue; // Do not remove OEM folders.
300 while (folder->item_list()->item_count()) {
301 scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
302 child->set_folder_id("");
303 AddItemToItemListAndNotifyUpdate(child.Pass());
305 folder_ids.push_back(folder->id());
307 // Delete folders.
308 for (size_t i = 0; i < folder_ids.size(); ++i)
309 DeleteItem(folder_ids[i]);
312 void AppListModel::SetCustomLauncherPageEnabled(bool enabled) {
313 custom_launcher_page_enabled_ = enabled;
314 FOR_EACH_OBSERVER(AppListModelObserver, observers_,
315 OnCustomLauncherPageEnabledStateChanged(enabled));
318 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
319 SearchResults* results,
320 SearchResult::DisplayType display_type,
321 size_t max_results) {
322 std::vector<SearchResult*> matches;
323 for (size_t i = 0; i < results->item_count(); ++i) {
324 SearchResult* item = results->GetItemAt(i);
325 if (item->display_type() == display_type) {
326 matches.push_back(item);
327 if (matches.size() == max_results)
328 break;
331 return matches;
334 void AppListModel::PushCustomLauncherPageSubpage() {
335 custom_launcher_page_subpage_depth_++;
338 bool AppListModel::PopCustomLauncherPageSubpage() {
339 if (custom_launcher_page_subpage_depth_ == 0)
340 return false;
342 --custom_launcher_page_subpage_depth_;
343 return true;
346 void AppListModel::ClearCustomLauncherPageSubpages() {
347 custom_launcher_page_subpage_depth_ = 0;
350 // Private methods
352 void AppListModel::OnListItemMoved(size_t from_index,
353 size_t to_index,
354 AppListItem* item) {
355 FOR_EACH_OBSERVER(AppListModelObserver,
356 observers_,
357 OnAppListItemUpdated(item));
360 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
361 const std::string& folder_id) {
362 if (folder_id.empty())
363 return NULL;
365 AppListFolderItem* dest_folder = FindFolderItem(folder_id);
366 if (dest_folder)
367 return dest_folder;
369 if (!folders_enabled()) {
370 LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
371 return NULL;
374 DVLOG(2) << "Creating new folder: " << folder_id;
375 scoped_ptr<AppListFolderItem> new_folder(
376 new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
377 new_folder->set_position(
378 top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
379 AppListItem* new_folder_item = AddItemToItemListAndNotify(new_folder.Pass());
380 return static_cast<AppListFolderItem*>(new_folder_item);
383 AppListItem* AppListModel::AddItemToItemListAndNotify(
384 scoped_ptr<AppListItem> item_ptr) {
385 DCHECK(!item_ptr->IsInFolder());
386 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
387 FOR_EACH_OBSERVER(AppListModelObserver,
388 observers_,
389 OnAppListItemAdded(item));
390 return item;
393 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
394 scoped_ptr<AppListItem> item_ptr) {
395 DCHECK(!item_ptr->IsInFolder());
396 AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
397 FOR_EACH_OBSERVER(AppListModelObserver,
398 observers_,
399 OnAppListItemUpdated(item));
400 return item;
403 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
404 AppListFolderItem* folder,
405 scoped_ptr<AppListItem> item_ptr) {
406 CHECK_NE(folder->id(), item_ptr->folder_id());
407 AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
408 item->set_folder_id(folder->id());
409 FOR_EACH_OBSERVER(AppListModelObserver,
410 observers_,
411 OnAppListItemUpdated(item));
412 return item;
415 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
416 if (!item->IsInFolder())
417 return top_level_item_list_->RemoveItem(item->id());
419 AppListFolderItem* folder = FindFolderItem(item->folder_id());
420 return RemoveItemFromFolder(folder, item);
423 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
424 AppListFolderItem* folder,
425 AppListItem* item) {
426 std::string folder_id = folder->id();
427 CHECK_EQ(item->folder_id(), folder_id);
428 scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
429 result->set_folder_id("");
430 if (folder->item_list()->item_count() == 0) {
431 DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
432 DeleteItem(folder_id);
434 return result.Pass();
437 } // namespace app_list