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 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
)
41 FOR_EACH_OBSERVER(AppListModelObserver
,
43 OnAppListModelStatusChanged());
46 void AppListModel::SetState(State state
) {
50 State old_state
= state_
;
54 FOR_EACH_OBSERVER(AppListModelObserver
,
56 OnAppListModelStateChanged(old_state
, state_
));
59 AppListItem
* AppListModel::FindItem(const std::string
& id
) {
60 AppListItem
* item
= top_level_item_list_
->FindItem(id
);
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
);
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
);
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
);
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.";
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
<< ").";
115 // Find the target item.
116 AppListItem
* target_item
= top_level_item_list_
->FindItem(target_item_id
);
118 LOG(ERROR
) << "MergeItems: Target no longer exists.";
122 AppListItem
* source_item
= FindItem(source_item_id
);
124 LOG(ERROR
) << "MergeItems: Source no longer exists.";
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";
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
)
182 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
183 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
185 CHECK(!item
->IsInFolder());
186 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
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
)
200 AppListFolderItem
* src_folder
= FindOrCreateFolderItem(item
->folder_id());
202 src_folder
->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM
) {
203 LOG(WARNING
) << "MoveItemToFolderAt called with OEM folder as source";
206 AppListFolderItem
* dest_folder
= FindOrCreateFolderItem(folder_id
);
207 scoped_ptr
<AppListItem
> item_ptr
= RemoveItem(item
);
209 item_ptr
->set_position(
210 dest_folder
->item_list()->CreatePositionBefore(position
));
211 AddItemToFolderItemAndNotify(dest_folder
, item_ptr
.Pass());
213 item_ptr
->set_position(
214 top_level_item_list_
->CreatePositionBefore(position
));
215 AddItemToItemListAndNotifyUpdate(item_ptr
.Pass());
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).
229 AppListFolderItem
* folder
= FindFolderItem(item
->folder_id());
231 folder
->item_list()->SetItemPosition(item
, new_position
);
232 FOR_EACH_OBSERVER(AppListModelObserver
,
234 OnAppListItemUpdated(item
));
237 void AppListModel::SetItemName(AppListItem
* item
, const std::string
& name
) {
239 DVLOG(2) << "AppListModel::SetItemName: " << item
->ToDebugString();
240 FOR_EACH_OBSERVER(AppListModelObserver
,
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
,
253 OnAppListItemUpdated(item
));
256 void AppListModel::DeleteItem(const std::string
& id
) {
257 AppListItem
* item
= FindItem(id
);
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
,
265 OnAppListItemWillBeDeleted(item
));
266 top_level_item_list_
->DeleteItem(id
);
267 FOR_EACH_OBSERVER(AppListModelObserver
, observers_
, OnAppListItemDeleted());
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
,
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
;
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
)
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());
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
)
329 void AppListModel::OnListItemMoved(size_t from_index
,
332 FOR_EACH_OBSERVER(AppListModelObserver
,
334 OnAppListItemUpdated(item
));
337 AppListFolderItem
* AppListModel::FindOrCreateFolderItem(
338 const std::string
& folder_id
) {
339 if (folder_id
.empty())
342 AppListFolderItem
* dest_folder
= FindFolderItem(folder_id
);
346 if (!folders_enabled()) {
347 LOG(ERROR
) << "Attempt to create folder item when disabled: " << folder_id
;
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
,
366 OnAppListItemAdded(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
,
376 OnAppListItemUpdated(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
,
388 OnAppListItemUpdated(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
,
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