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"
13 #include "ui/app_list/search_result.h"
17 AppListModel::AppListModel()
18 : top_level_item_list_(new AppListItemList
),
19 search_box_(new SearchBoxModel
),
20 results_(new SearchResults
),
21 status_(STATUS_NORMAL
) {
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 DCHECK(!item
->IsInFolder() || item
->folder_id() == folder_id
);
78 DCHECK(item
->GetItemType() != AppListFolderItem::kItemType
);
79 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
80 DCHECK(!dest_folder
->item_list()->FindItem(item
->id()))
81 << "Already in folder: " << dest_folder
->id();
82 return AddItemToFolderItemAndNotify(dest_folder
, item
.Pass());
85 const std::string
AppListModel::MergeItems(const std::string
& target_item_id
,
86 const std::string
& source_item_id
) {
87 DVLOG(2) << "MergeItems: " << source_item_id
<< " -> " << target_item_id
;
88 // Find the target item.
89 AppListItem
* target_item
= FindItem(target_item_id
);
91 LOG(ERROR
) << "MergeItems: Target no longer exists.";
94 CHECK(target_item
->folder_id().empty());
96 AppListItem
* source_item
= FindItem(source_item_id
);
98 LOG(ERROR
) << "MergeItems: Source no longer exists.";
102 // If the target item is a folder, just add the source item to it.
103 if (target_item
->GetItemType() == AppListFolderItem::kItemType
) {
104 AppListFolderItem
* target_folder
=
105 static_cast<AppListFolderItem
*>(target_item
);
106 if (target_folder
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
) {
107 LOG(WARNING
) << "MergeItems called with OEM folder as target";
110 scoped_ptr
<AppListItem
> source_item_ptr
= RemoveItem(source_item
);
111 source_item_ptr
->set_position(
112 target_folder
->item_list()->CreatePositionBefore(
113 syncer::StringOrdinal()));
114 AddItemToFolderItemAndNotify(target_folder
, source_item_ptr
.Pass());
115 return target_folder
->id();
118 // Otherwise remove the source item and target item from their current
119 // location, they will become owned by the new folder.
120 scoped_ptr
<AppListItem
> source_item_ptr
= RemoveItem(source_item
);
121 CHECK(source_item_ptr
);
122 scoped_ptr
<AppListItem
> target_item_ptr
=
123 top_level_item_list_
->RemoveItem(target_item_id
);
124 CHECK(target_item_ptr
);
126 // Create a new folder in the same location as the target item.
127 std::string new_folder_id
= AppListFolderItem::GenerateId();
128 DVLOG(2) << "Creating folder for merge: " << new_folder_id
;
129 scoped_ptr
<AppListItem
> new_folder_ptr(new AppListFolderItem(
130 new_folder_id
, AppListFolderItem::FOLDER_TYPE_NORMAL
));
131 new_folder_ptr
->set_position(target_item_ptr
->position());
132 AppListFolderItem
* new_folder
= static_cast<AppListFolderItem
*>(
133 AddItemToItemListAndNotify(new_folder_ptr
.Pass()));
135 // Add the items to the new folder.
136 target_item_ptr
->set_position(
137 new_folder
->item_list()->CreatePositionBefore(
138 syncer::StringOrdinal()));
139 AddItemToFolderItemAndNotify(new_folder
, target_item_ptr
.Pass());
140 source_item_ptr
->set_position(
141 new_folder
->item_list()->CreatePositionBefore(
142 syncer::StringOrdinal()));
143 AddItemToFolderItemAndNotify(new_folder
, source_item_ptr
.Pass());
145 return new_folder
->id();
148 void AppListModel::MoveItemToFolder(AppListItem
* item
,
149 const std::string
& folder_id
) {
150 DVLOG(2) << "MoveItemToFolder: " << folder_id
151 << " <- " << item
->ToDebugString();
152 if (item
->folder_id() == folder_id
)
154 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
155 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
157 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
159 AddItemToItemListAndNotifyUpdate(item_ptr
.Pass());
162 bool AppListModel::MoveItemToFolderAt(AppListItem
* item
,
163 const std::string
& folder_id
,
164 syncer::StringOrdinal position
) {
165 DVLOG(2) << "MoveItemToFolderAt: " << folder_id
166 << "[" << position
.ToDebugString() << "]"
167 << " <- " << item
->ToDebugString();
168 if (item
->folder_id() == folder_id
)
170 AppListFolderItem
* src_folder
= FindOrCreateFolderItem(item
->folder_id());
172 src_folder
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
) {
173 LOG(WARNING
) << "MoveItemToFolderAt called with OEM folder as source";
176 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
177 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
179 item_ptr
->set_position(
180 dest_folder
->item_list()->CreatePositionBefore(position
));
181 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
183 item_ptr
->set_position(
184 top_level_item_list_
->CreatePositionBefore(position
));
185 AddItemToItemListAndNotifyUpdate(item_ptr
.Pass());
190 void AppListModel::SetItemPosition(AppListItem
* item
,
191 const syncer::StringOrdinal
& new_position
) {
192 if (!item
->IsInFolder()) {
193 top_level_item_list_
->SetItemPosition(item
, new_position
);
194 // Note: this will trigger OnListItemMoved which will signal observers.
195 // (This is done this way because some View code still moves items within
196 // the item list directly).
199 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
201 folder
->item_list()->SetItemPosition(item
, new_position
);
202 FOR_EACH_OBSERVER(AppListModelObserver
,
204 OnAppListItemUpdated(item
));
207 void AppListModel::SetItemName(AppListItem
* item
, const std::string
& name
) {
209 DVLOG(2) << "AppListModel::SetItemName: " << item
->ToDebugString();
210 FOR_EACH_OBSERVER(AppListModelObserver
,
212 OnAppListItemUpdated(item
));
215 void AppListModel::SetItemNameAndShortName(AppListItem
* item
,
216 const std::string
& name
,
217 const std::string
& short_name
) {
218 item
->SetNameAndShortName(name
, short_name
);
219 DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
220 << item
->ToDebugString();
221 FOR_EACH_OBSERVER(AppListModelObserver
,
223 OnAppListItemUpdated(item
));
226 void AppListModel::DeleteItem(const std::string
& id
) {
227 AppListItem
* item
= FindItem(id
);
230 if (!item
->IsInFolder()) {
231 DCHECK_EQ(0u, item
->ChildItemCount())
232 << "Invalid call to DeleteItem for item with children: " << id
;
233 FOR_EACH_OBSERVER(AppListModelObserver
,
235 OnAppListItemWillBeDeleted(item
));
236 top_level_item_list_
->DeleteItem(id
);
239 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
240 DCHECK(folder
) << "Folder not found for item: " << item
->ToDebugString();
241 scoped_ptr
<AppListItem
> child_item
= RemoveItemFromFolder(folder
, item
);
242 DCHECK_EQ(item
, child_item
.get());
243 FOR_EACH_OBSERVER(AppListModelObserver
,
245 OnAppListItemWillBeDeleted(item
));
246 child_item
.reset(); // Deletes item.
249 void AppListModel::NotifyExtensionPreferenceChanged() {
250 for (size_t i
= 0; i
< top_level_item_list_
->item_count(); ++i
)
251 top_level_item_list_
->item_at(i
)->OnExtensionPreferenceChanged();
256 void AppListModel::OnListItemMoved(size_t from_index
,
259 FOR_EACH_OBSERVER(AppListModelObserver
,
261 OnAppListItemUpdated(item
));
264 AppListFolderItem
* AppListModel::FindOrCreateFolderItem(
265 const std::string
& folder_id
) {
266 if (folder_id
.empty())
269 AppListFolderItem
* dest_folder
= FindFolderItem(folder_id
);
273 DVLOG(2) << "Creating new folder: " << folder_id
;
274 scoped_ptr
<AppListFolderItem
> new_folder(
275 new AppListFolderItem(folder_id
, AppListFolderItem::FOLDER_TYPE_NORMAL
));
276 new_folder
->set_position(
277 top_level_item_list_
->CreatePositionBefore(syncer::StringOrdinal()));
278 AppListItem
* new_folder_item
=
279 AddItemToItemListAndNotify(new_folder
.PassAs
<AppListItem
>());
280 return static_cast<AppListFolderItem
*>(new_folder_item
);
283 AppListItem
* AppListModel::AddItemToItemListAndNotify(
284 scoped_ptr
<AppListItem
> item_ptr
) {
285 DCHECK(!item_ptr
->IsInFolder());
286 AppListItem
* item
= top_level_item_list_
->AddItem(item_ptr
.Pass());
287 FOR_EACH_OBSERVER(AppListModelObserver
,
289 OnAppListItemAdded(item
));
293 AppListItem
* AppListModel::AddItemToItemListAndNotifyUpdate(
294 scoped_ptr
<AppListItem
> item_ptr
) {
295 DCHECK(!item_ptr
->IsInFolder());
296 AppListItem
* item
= top_level_item_list_
->AddItem(item_ptr
.Pass());
297 FOR_EACH_OBSERVER(AppListModelObserver
,
299 OnAppListItemUpdated(item
));
303 AppListItem
* AppListModel::AddItemToFolderItemAndNotify(
304 AppListFolderItem
* folder
,
305 scoped_ptr
<AppListItem
> item_ptr
) {
306 AppListItem
* item
= folder
->item_list()->AddItem(item_ptr
.Pass());
307 item
->set_folder_id(folder
->id());
308 FOR_EACH_OBSERVER(AppListModelObserver
,
310 OnAppListItemUpdated(item
));
314 scoped_ptr
<AppListItem
> AppListModel::RemoveItem(AppListItem
* item
) {
315 if (!item
->IsInFolder())
316 return top_level_item_list_
->RemoveItem(item
->id());
318 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
319 return RemoveItemFromFolder(folder
, item
);
322 scoped_ptr
<AppListItem
> AppListModel::RemoveItemFromFolder(
323 AppListFolderItem
* folder
,
325 std::string folder_id
= folder
->id();
326 DCHECK_EQ(item
->folder_id(), folder_id
);
327 scoped_ptr
<AppListItem
> result
= folder
->item_list()->RemoveItem(item
->id());
328 result
->set_folder_id("");
329 if (folder
->item_list()->item_count() == 0) {
330 DVLOG(2) << "Deleting empty folder: " << folder
->ToDebugString();
331 DeleteItem(folder_id
);
333 return result
.Pass();
336 } // namespace app_list