app_list_model.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5#include "ui/app_list/app_list_model.h"
6
7#include "ui/app_list/app_list_folder_item.h"
8#include "ui/app_list/app_list_item.h"
9#include "ui/app_list/app_list_model_observer.h"
10#include "ui/app_list/search_box_model.h"
11#include "ui/app_list/search_result.h"
12
13namespace app_list {
14
15AppListModel::AppListModel()
16    : item_list_(new AppListItemList),
17      search_box_(new SearchBoxModel),
18      results_(new SearchResults),
19      status_(STATUS_NORMAL) {
20  item_list_->AddObserver(this);
21}
22
23AppListModel::~AppListModel() {
24  item_list_->RemoveObserver(this);
25}
26
27void AppListModel::AddObserver(AppListModelObserver* observer) {
28  observers_.AddObserver(observer);
29}
30
31void AppListModel::RemoveObserver(AppListModelObserver* observer) {
32  observers_.RemoveObserver(observer);
33}
34
35void AppListModel::SetStatus(Status status) {
36  if (status_ == status)
37    return;
38
39  status_ = status;
40  FOR_EACH_OBSERVER(AppListModelObserver,
41                    observers_,
42                    OnAppListModelStatusChanged());
43}
44
45AppListItem* AppListModel::FindItem(const std::string& id) {
46  AppListItem* item = item_list_->FindItem(id);
47  if (item)
48    return item;
49  for (size_t i = 0; i < item_list_->item_count(); ++i) {
50    AppListItem* child_item = item_list_->item_at(i)->FindChildItem(id);
51    if (child_item)
52      return child_item;
53  }
54  return NULL;
55}
56
57AppListFolderItem* AppListModel::FindFolderItem(const std::string& id) {
58  AppListItem* item = item_list_->FindItem(id);
59  if (item && item->GetItemType() == AppListFolderItem::kItemType)
60    return static_cast<AppListFolderItem*>(item);
61  DCHECK(!item);
62  return NULL;
63}
64
65AppListItem* AppListModel::AddItem(scoped_ptr<AppListItem> item) {
66  DCHECK(!item->IsInFolder());
67  DCHECK(!item_list()->FindItem(item->id()));
68  return AddItemToItemListAndNotify(item.Pass());
69}
70
71AppListItem* AppListModel::AddItemToFolder(scoped_ptr<AppListItem> item,
72                                           const std::string& folder_id) {
73  if (folder_id.empty())
74    return AddItem(item.Pass());
75  DCHECK(!item->IsInFolder() || item->folder_id() == folder_id);
76  DCHECK(item->GetItemType() != AppListFolderItem::kItemType);
77  AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
78  DCHECK(!dest_folder->item_list()->FindItem(item->id()));
79  return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
80}
81
82const std::string& AppListModel::MergeItems(const std::string& target_item_id,
83                                            const std::string& source_item_id) {
84  DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
85  // First, remove the source item from the model.
86  scoped_ptr<AppListItem> source_item_ptr =
87      RemoveItem(FindItem(source_item_id));
88
89  // Next, find the target item.
90  AppListItem* target_item = FindItem(target_item_id);
91  DCHECK(target_item);
92  DCHECK(target_item->folder_id().empty());
93
94  // If the target item is a folder, just add |source_item| to it.
95  if (target_item->GetItemType() == AppListFolderItem::kItemType) {
96    AppListFolderItem* target_folder =
97        static_cast<AppListFolderItem*>(target_item);
98    source_item_ptr->set_position(
99        target_folder->item_list()->CreatePositionBefore(
100            syncer::StringOrdinal()));
101    AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
102    return target_folder->id();
103  }
104
105  // Otherwise, remove the target item from |item_list_|, it will become owned
106  // by the new folder.
107  scoped_ptr<AppListItem> target_item_ptr =
108      item_list_->RemoveItem(target_item_id);
109
110  // Create a new folder in the same location as the target item.
111  scoped_ptr<AppListItem> new_folder_ptr(
112      new AppListFolderItem(AppListFolderItem::GenerateId()));
113  new_folder_ptr->set_position(target_item->position());
114  AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
115      AddItemToItemListAndNotify(new_folder_ptr.Pass()));
116
117  // Add the items to the new folder.
118  target_item_ptr->set_position(
119      new_folder->item_list()->CreatePositionBefore(
120          syncer::StringOrdinal()));
121  AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
122  source_item_ptr->set_position(
123      new_folder->item_list()->CreatePositionBefore(
124          syncer::StringOrdinal()));
125  AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
126
127  return new_folder->id();
128}
129
130void AppListModel::MoveItemToFolder(AppListItem* item,
131                                    const std::string& folder_id) {
132  DVLOG(2) << "MoveItemToFolder: " << folder_id
133           << " <- " << item->ToDebugString();
134  if (item->folder_id() == folder_id)
135    return;
136  AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
137  scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
138  if (dest_folder)
139    AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
140  else
141    AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
142}
143
144void AppListModel::MoveItemToFolderAt(AppListItem* item,
145                                      const std::string& folder_id,
146                                      syncer::StringOrdinal position) {
147  DVLOG(2) << "MoveItemToFolderAt: " << folder_id
148           << "[" << position.ToDebugString() << "]"
149           << " <- " << item->ToDebugString();
150  if (item->folder_id() == folder_id)
151    return;
152  AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
153  scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
154  if (dest_folder) {
155    item_ptr->set_position(
156        dest_folder->item_list()->CreatePositionBefore(position));
157    AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
158  } else {
159    item_ptr->set_position(item_list_->CreatePositionBefore(position));
160    AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
161  }
162}
163
164void AppListModel::SetItemPosition(AppListItem* item,
165                                   const syncer::StringOrdinal& new_position) {
166  if (!item->IsInFolder()) {
167    item_list_->SetItemPosition(item, new_position);
168    // Note: this will trigger OnListItemMoved which will signal observers.
169    // (This is done this way because some View code still moves items within
170    // the item list directly).
171    return;
172  }
173  AppListFolderItem* folder = FindFolderItem(item->folder_id());
174  DCHECK(folder);
175  folder->item_list()->SetItemPosition(item, new_position);
176  FOR_EACH_OBSERVER(AppListModelObserver,
177                    observers_,
178                    OnAppListItemUpdated(item));
179}
180
181void AppListModel::DeleteItem(const std::string& id) {
182  AppListItem* item = FindItem(id);
183  if (!item)
184    return;
185  if (!item->IsInFolder()) {
186    DCHECK_EQ(0u, item->ChildItemCount())
187        << "Invalid call to DeleteItem for item with children: " << id;
188    FOR_EACH_OBSERVER(AppListModelObserver,
189                      observers_,
190                      OnAppListItemWillBeDeleted(item));
191    item_list_->DeleteItem(id);
192    return;
193  }
194  AppListFolderItem* folder = FindFolderItem(item->folder_id());
195  DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
196  scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
197  DCHECK_EQ(item, child_item.get());
198  FOR_EACH_OBSERVER(AppListModelObserver,
199                    observers_,
200                    OnAppListItemWillBeDeleted(item));
201  child_item.reset();  // Deletes item.
202}
203
204// Private methods
205
206void AppListModel::OnListItemMoved(size_t from_index,
207                                   size_t to_index,
208                                   AppListItem* item) {
209  FOR_EACH_OBSERVER(AppListModelObserver,
210                    observers_,
211                    OnAppListItemUpdated(item));
212}
213
214AppListFolderItem* AppListModel::FindOrCreateFolderItem(
215    const std::string& folder_id) {
216  if (folder_id.empty())
217    return NULL;
218
219  AppListFolderItem* dest_folder = FindFolderItem(folder_id);
220  if (dest_folder)
221    return dest_folder;
222
223  scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(folder_id));
224  new_folder->set_position(
225      item_list_->CreatePositionBefore(syncer::StringOrdinal()));
226  AppListItem* new_folder_item =
227      AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>());
228  return static_cast<AppListFolderItem*>(new_folder_item);
229}
230
231AppListItem* AppListModel::AddItemToItemListAndNotify(
232    scoped_ptr<AppListItem> item_ptr) {
233  DCHECK(!item_ptr->IsInFolder());
234  AppListItem* item = item_list_->AddItem(item_ptr.Pass());
235  FOR_EACH_OBSERVER(AppListModelObserver,
236                    observers_,
237                    OnAppListItemAdded(item));
238  return item;
239}
240
241AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
242    scoped_ptr<AppListItem> item_ptr) {
243  DCHECK(!item_ptr->IsInFolder());
244  AppListItem* item = item_list_->AddItem(item_ptr.Pass());
245  FOR_EACH_OBSERVER(AppListModelObserver,
246                    observers_,
247                    OnAppListItemUpdated(item));
248  return item;
249}
250
251AppListItem* AppListModel::AddItemToFolderItemAndNotify(
252    AppListFolderItem* folder,
253    scoped_ptr<AppListItem> item_ptr) {
254  AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
255  item->set_folder_id(folder->id());
256  FOR_EACH_OBSERVER(AppListModelObserver,
257                    observers_,
258                    OnAppListItemUpdated(item));
259  return item;
260}
261
262scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
263  if (!item->IsInFolder())
264    return item_list_->RemoveItem(item->id());
265
266  AppListFolderItem* folder = FindFolderItem(item->folder_id());
267  DCHECK(folder);
268  return RemoveItemFromFolder(folder, item);
269}
270
271scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
272    AppListFolderItem* folder,
273    AppListItem* item) {
274  std::string folder_id = folder->id();
275  DCHECK_EQ(item->folder_id(), folder_id);
276  scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
277  result->set_folder_id("");
278  if (folder->item_list()->item_count() == 0) {
279    DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
280    DeleteItem(folder_id);
281  }
282  return result.Pass();
283}
284
285}  // namespace app_list
286