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"
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"
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
)
42 FOR_EACH_OBSERVER(AppListModelObserver
,
44 OnAppListModelStatusChanged());
47 void AppListModel::SetState(State state
) {
51 State old_state
= state_
;
55 FOR_EACH_OBSERVER(AppListModelObserver
,
57 OnAppListModelStateChanged(old_state
, state_
));
60 AppListItem
* AppListModel::FindItem(const std::string
& id
) {
61 AppListItem
* item
= top_level_item_list_
->FindItem(id
);
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
);
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
);
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
);
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.";
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
<< ").";
116 // Find the target item.
117 AppListItem
* target_item
= top_level_item_list_
->FindItem(target_item_id
);
119 LOG(ERROR
) << "MergeItems: Target no longer exists.";
123 AppListItem
* source_item
= FindItem(source_item_id
);
125 LOG(ERROR
) << "MergeItems: Source no longer exists.";
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";
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
)
183 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
184 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
186 CHECK(!item
->IsInFolder());
187 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
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
)
201 AppListFolderItem
* src_folder
= FindOrCreateFolderItem(item
->folder_id());
203 src_folder
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
) {
204 LOG(WARNING
) << "MoveItemToFolderAt called with OEM folder as source";
207 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
208 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
210 item_ptr
->set_position(
211 dest_folder
->item_list()->CreatePositionBefore(position
));
212 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
214 item_ptr
->set_position(
215 top_level_item_list_
->CreatePositionBefore(position
));
216 AddItemToItemListAndNotifyUpdate(item_ptr
.Pass());
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).
230 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
232 folder
->item_list()->SetItemPosition(item
, new_position
);
233 FOR_EACH_OBSERVER(AppListModelObserver
,
235 OnAppListItemUpdated(item
));
238 void AppListModel::SetItemName(AppListItem
* item
, const std::string
& name
) {
240 DVLOG(2) << "AppListModel::SetItemName: " << item
->ToDebugString();
241 FOR_EACH_OBSERVER(AppListModelObserver
,
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
,
254 OnAppListItemUpdated(item
));
257 void AppListModel::DeleteItem(const std::string
& id
) {
258 AppListItem
* item
= FindItem(id
);
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
,
266 OnAppListItemWillBeDeleted(item
));
267 top_level_item_list_
->DeleteItem(id
);
268 FOR_EACH_OBSERVER(AppListModelObserver
, observers_
, OnAppListItemDeleted());
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
,
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
;
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
)
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());
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
)
334 void AppListModel::PushCustomLauncherPageSubpage() {
335 custom_launcher_page_subpage_depth_
++;
338 bool AppListModel::PopCustomLauncherPageSubpage() {
339 if (custom_launcher_page_subpage_depth_
== 0)
342 --custom_launcher_page_subpage_depth_
;
346 void AppListModel::ClearCustomLauncherPageSubpages() {
347 custom_launcher_page_subpage_depth_
= 0;
352 void AppListModel::OnListItemMoved(size_t from_index
,
355 FOR_EACH_OBSERVER(AppListModelObserver
,
357 OnAppListItemUpdated(item
));
360 AppListFolderItem
* AppListModel::FindOrCreateFolderItem(
361 const std::string
& folder_id
) {
362 if (folder_id
.empty())
365 AppListFolderItem
* dest_folder
= FindFolderItem(folder_id
);
369 if (!folders_enabled()) {
370 LOG(ERROR
) << "Attempt to create folder item when disabled: " << folder_id
;
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
,
389 OnAppListItemAdded(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
,
399 OnAppListItemUpdated(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
,
411 OnAppListItemUpdated(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
,
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