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 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
)
40 FOR_EACH_OBSERVER(AppListModelObserver
,
42 OnAppListModelStatusChanged());
45 AppListItem
* AppListModel::FindItem(const std::string
& id
) {
46 AppListItem
* item
= top_level_item_list_
->FindItem(id
);
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
);
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
);
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
);
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.";
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
<< ").";
101 // Find the target item.
102 AppListItem
* target_item
= top_level_item_list_
->FindItem(target_item_id
);
104 LOG(ERROR
) << "MergeItems: Target no longer exists.";
108 AppListItem
* source_item
= FindItem(source_item_id
);
110 LOG(ERROR
) << "MergeItems: Source no longer exists.";
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";
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
)
168 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
169 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
171 CHECK(!item
->IsInFolder());
172 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
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
)
186 AppListFolderItem
* src_folder
= FindOrCreateFolderItem(item
->folder_id());
188 src_folder
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
) {
189 LOG(WARNING
) << "MoveItemToFolderAt called with OEM folder as source";
192 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
193 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
195 item_ptr
->set_position(
196 dest_folder
->item_list()->CreatePositionBefore(position
));
197 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
199 item_ptr
->set_position(
200 top_level_item_list_
->CreatePositionBefore(position
));
201 AddItemToItemListAndNotifyUpdate(item_ptr
.Pass());
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).
215 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
217 folder
->item_list()->SetItemPosition(item
, new_position
);
218 FOR_EACH_OBSERVER(AppListModelObserver
,
220 OnAppListItemUpdated(item
));
223 void AppListModel::SetItemName(AppListItem
* item
, const std::string
& name
) {
225 DVLOG(2) << "AppListModel::SetItemName: " << item
->ToDebugString();
226 FOR_EACH_OBSERVER(AppListModelObserver
,
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
,
239 OnAppListItemUpdated(item
));
242 void AppListModel::DeleteItem(const std::string
& id
) {
243 AppListItem
* item
= FindItem(id
);
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
,
251 OnAppListItemWillBeDeleted(item
));
252 top_level_item_list_
->DeleteItem(id
);
253 FOR_EACH_OBSERVER(AppListModelObserver
, observers_
, OnAppListItemDeleted());
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
,
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
;
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
)
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());
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
)
315 void AppListModel::OnListItemMoved(size_t from_index
,
318 FOR_EACH_OBSERVER(AppListModelObserver
,
320 OnAppListItemUpdated(item
));
323 AppListFolderItem
* AppListModel::FindOrCreateFolderItem(
324 const std::string
& folder_id
) {
325 if (folder_id
.empty())
328 AppListFolderItem
* dest_folder
= FindFolderItem(folder_id
);
332 if (!folders_enabled()) {
333 LOG(ERROR
) << "Attempt to create folder item when disabled: " << folder_id
;
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
,
352 OnAppListItemAdded(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
,
362 OnAppListItemUpdated(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
,
374 OnAppListItemUpdated(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
,
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