22ac8a25634973496035b81e118e10ef1e14ad89
[platform/framework/web/crosswalk.git] / src / ui / app_list / app_list_model.cc
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 <string>
8
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
14 namespace app_list {
15
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);
23 }
24
25 AppListModel::~AppListModel() { top_level_item_list_->RemoveObserver(this); }
26
27 void AppListModel::AddObserver(AppListModelObserver* observer) {
28   observers_.AddObserver(observer);
29 }
30
31 void AppListModel::RemoveObserver(AppListModelObserver* observer) {
32   observers_.RemoveObserver(observer);
33 }
34
35 void 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
45 AppListItem* AppListModel::FindItem(const std::string& id) {
46   AppListItem* item = top_level_item_list_->FindItem(id);
47   if (item)
48     return item;
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);
52     if (child_item)
53       return child_item;
54   }
55   return NULL;
56 }
57
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);
62   DCHECK(!item);
63   return NULL;
64 }
65
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());
70 }
71
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);
80   if (!dest_folder)
81     return NULL;
82   DCHECK(!dest_folder->item_list()->FindItem(item->id()))
83       << "Already in folder: " << dest_folder->id();
84   return AddItemToFolderItemAndNotify(dest_folder, item.Pass());
85 }
86
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.";
91     return "";
92   }
93   DVLOG(2) << "MergeItems: " << source_item_id << " -> " << target_item_id;
94   // Find the target item.
95   AppListItem* target_item = FindItem(target_item_id);
96   if (!target_item) {
97     LOG(ERROR) << "MergeItems: Target no longer exists.";
98     return "";
99   }
100   CHECK(target_item->folder_id().empty());
101
102   AppListItem* source_item = FindItem(source_item_id);
103   if (!source_item) {
104     LOG(ERROR) << "MergeItems: Source no longer exists.";
105     return "";
106   }
107
108   // If the target item is a folder, just add the source item to it.
109   if (target_item->GetItemType() == AppListFolderItem::kItemType) {
110     AppListFolderItem* target_folder =
111         static_cast<AppListFolderItem*>(target_item);
112     if (target_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
113       LOG(WARNING) << "MergeItems called with OEM folder as target";
114       return "";
115     }
116     scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
117     source_item_ptr->set_position(
118         target_folder->item_list()->CreatePositionBefore(
119             syncer::StringOrdinal()));
120     AddItemToFolderItemAndNotify(target_folder, source_item_ptr.Pass());
121     return target_folder->id();
122   }
123
124   // Otherwise remove the source item and target item from their current
125   // location, they will become owned by the new folder.
126   scoped_ptr<AppListItem> source_item_ptr = RemoveItem(source_item);
127   CHECK(source_item_ptr);
128   scoped_ptr<AppListItem> target_item_ptr =
129       top_level_item_list_->RemoveItem(target_item_id);
130   CHECK(target_item_ptr);
131
132   // Create a new folder in the same location as the target item.
133   std::string new_folder_id = AppListFolderItem::GenerateId();
134   DVLOG(2) << "Creating folder for merge: " << new_folder_id;
135   scoped_ptr<AppListItem> new_folder_ptr(new AppListFolderItem(
136       new_folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
137   new_folder_ptr->set_position(target_item_ptr->position());
138   AppListFolderItem* new_folder = static_cast<AppListFolderItem*>(
139       AddItemToItemListAndNotify(new_folder_ptr.Pass()));
140
141   // Add the items to the new folder.
142   target_item_ptr->set_position(
143       new_folder->item_list()->CreatePositionBefore(
144           syncer::StringOrdinal()));
145   AddItemToFolderItemAndNotify(new_folder, target_item_ptr.Pass());
146   source_item_ptr->set_position(
147       new_folder->item_list()->CreatePositionBefore(
148           syncer::StringOrdinal()));
149   AddItemToFolderItemAndNotify(new_folder, source_item_ptr.Pass());
150
151   return new_folder->id();
152 }
153
154 void AppListModel::MoveItemToFolder(AppListItem* item,
155                                     const std::string& folder_id) {
156   DVLOG(2) << "MoveItemToFolder: " << folder_id
157            << " <- " << item->ToDebugString();
158   if (item->folder_id() == folder_id)
159     return;
160   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
161   scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
162   if (dest_folder) {
163     CHECK(!item->IsInFolder());
164     AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
165   } else {
166     AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
167   }
168 }
169
170 bool AppListModel::MoveItemToFolderAt(AppListItem* item,
171                                       const std::string& folder_id,
172                                       syncer::StringOrdinal position) {
173   DVLOG(2) << "MoveItemToFolderAt: " << folder_id
174            << "[" << position.ToDebugString() << "]"
175            << " <- " << item->ToDebugString();
176   if (item->folder_id() == folder_id)
177     return false;
178   AppListFolderItem* src_folder = FindOrCreateFolderItem(item->folder_id());
179   if (src_folder &&
180       src_folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM) {
181     LOG(WARNING) << "MoveItemToFolderAt called with OEM folder as source";
182     return false;
183   }
184   AppListFolderItem* dest_folder = FindOrCreateFolderItem(folder_id);
185   scoped_ptr<AppListItem> item_ptr = RemoveItem(item);
186   if (dest_folder) {
187     item_ptr->set_position(
188         dest_folder->item_list()->CreatePositionBefore(position));
189     AddItemToFolderItemAndNotify(dest_folder, item_ptr.Pass());
190   } else {
191     item_ptr->set_position(
192         top_level_item_list_->CreatePositionBefore(position));
193     AddItemToItemListAndNotifyUpdate(item_ptr.Pass());
194   }
195   return true;
196 }
197
198 void AppListModel::SetItemPosition(AppListItem* item,
199                                    const syncer::StringOrdinal& new_position) {
200   if (!item->IsInFolder()) {
201     top_level_item_list_->SetItemPosition(item, new_position);
202     // Note: this will trigger OnListItemMoved which will signal observers.
203     // (This is done this way because some View code still moves items within
204     // the item list directly).
205     return;
206   }
207   AppListFolderItem* folder = FindFolderItem(item->folder_id());
208   DCHECK(folder);
209   folder->item_list()->SetItemPosition(item, new_position);
210   FOR_EACH_OBSERVER(AppListModelObserver,
211                     observers_,
212                     OnAppListItemUpdated(item));
213 }
214
215 void AppListModel::SetItemName(AppListItem* item, const std::string& name) {
216   item->SetName(name);
217   DVLOG(2) << "AppListModel::SetItemName: " << item->ToDebugString();
218   FOR_EACH_OBSERVER(AppListModelObserver,
219                     observers_,
220                     OnAppListItemUpdated(item));
221 }
222
223 void AppListModel::SetItemNameAndShortName(AppListItem* item,
224                                            const std::string& name,
225                                            const std::string& short_name) {
226   item->SetNameAndShortName(name, short_name);
227   DVLOG(2) << "AppListModel::SetItemNameAndShortName: "
228            << item->ToDebugString();
229   FOR_EACH_OBSERVER(AppListModelObserver,
230                     observers_,
231                     OnAppListItemUpdated(item));
232 }
233
234 void AppListModel::DeleteItem(const std::string& id) {
235   AppListItem* item = FindItem(id);
236   if (!item)
237     return;
238   if (!item->IsInFolder()) {
239     DCHECK_EQ(0u, item->ChildItemCount())
240         << "Invalid call to DeleteItem for item with children: " << id;
241     FOR_EACH_OBSERVER(AppListModelObserver,
242                       observers_,
243                       OnAppListItemWillBeDeleted(item));
244     top_level_item_list_->DeleteItem(id);
245     FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
246     return;
247   }
248   AppListFolderItem* folder = FindFolderItem(item->folder_id());
249   DCHECK(folder) << "Folder not found for item: " << item->ToDebugString();
250   scoped_ptr<AppListItem> child_item = RemoveItemFromFolder(folder, item);
251   DCHECK_EQ(item, child_item.get());
252   FOR_EACH_OBSERVER(AppListModelObserver,
253                     observers_,
254                     OnAppListItemWillBeDeleted(item));
255   child_item.reset();  // Deletes item.
256   FOR_EACH_OBSERVER(AppListModelObserver, observers_, OnAppListItemDeleted());
257 }
258
259 void AppListModel::NotifyExtensionPreferenceChanged() {
260   for (size_t i = 0; i < top_level_item_list_->item_count(); ++i)
261     top_level_item_list_->item_at(i)->OnExtensionPreferenceChanged();
262 }
263
264 void AppListModel::SetFoldersEnabled(bool folders_enabled) {
265   folders_enabled_ = folders_enabled;
266   if (folders_enabled)
267     return;
268   // Remove child items from folders.
269   std::vector<std::string> folder_ids;
270   for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
271     AppListItem* item = top_level_item_list_->item_at(i);
272     if (item->GetItemType() != AppListFolderItem::kItemType)
273       continue;
274     AppListFolderItem* folder = static_cast<AppListFolderItem*>(item);
275     if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
276       continue;  // Do not remove OEM folders.
277     while (folder->item_list()->item_count()) {
278       scoped_ptr<AppListItem> child = folder->item_list()->RemoveItemAt(0);
279       child->set_folder_id("");
280       AddItemToItemListAndNotifyUpdate(child.Pass());
281     }
282     folder_ids.push_back(folder->id());
283   }
284   // Delete folders.
285   for (size_t i = 0; i < folder_ids.size(); ++i)
286     DeleteItem(folder_ids[i]);
287 }
288
289 std::vector<SearchResult*> AppListModel::FilterSearchResultsByDisplayType(
290     SearchResults* results,
291     SearchResult::DisplayType display_type,
292     size_t max_results) {
293   std::vector<SearchResult*> matches;
294   for (size_t i = 0; i < results->item_count(); ++i) {
295     SearchResult* item = results->GetItemAt(i);
296     if (item->display_type() == display_type) {
297       matches.push_back(item);
298       if (matches.size() == max_results)
299         break;
300     }
301   }
302   return matches;
303 }
304
305 // Private methods
306
307 void AppListModel::OnListItemMoved(size_t from_index,
308                                    size_t to_index,
309                                    AppListItem* item) {
310   FOR_EACH_OBSERVER(AppListModelObserver,
311                     observers_,
312                     OnAppListItemUpdated(item));
313 }
314
315 AppListFolderItem* AppListModel::FindOrCreateFolderItem(
316     const std::string& folder_id) {
317   if (folder_id.empty())
318     return NULL;
319
320   AppListFolderItem* dest_folder = FindFolderItem(folder_id);
321   if (dest_folder)
322     return dest_folder;
323
324   if (!folders_enabled()) {
325     LOG(ERROR) << "Attempt to create folder item when disabled: " << folder_id;
326     return NULL;
327   }
328
329   DVLOG(2) << "Creating new folder: " << folder_id;
330   scoped_ptr<AppListFolderItem> new_folder(
331       new AppListFolderItem(folder_id, AppListFolderItem::FOLDER_TYPE_NORMAL));
332   new_folder->set_position(
333       top_level_item_list_->CreatePositionBefore(syncer::StringOrdinal()));
334   AppListItem* new_folder_item =
335       AddItemToItemListAndNotify(new_folder.PassAs<AppListItem>());
336   return static_cast<AppListFolderItem*>(new_folder_item);
337 }
338
339 AppListItem* AppListModel::AddItemToItemListAndNotify(
340     scoped_ptr<AppListItem> item_ptr) {
341   DCHECK(!item_ptr->IsInFolder());
342   AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
343   FOR_EACH_OBSERVER(AppListModelObserver,
344                     observers_,
345                     OnAppListItemAdded(item));
346   return item;
347 }
348
349 AppListItem* AppListModel::AddItemToItemListAndNotifyUpdate(
350     scoped_ptr<AppListItem> item_ptr) {
351   DCHECK(!item_ptr->IsInFolder());
352   AppListItem* item = top_level_item_list_->AddItem(item_ptr.Pass());
353   FOR_EACH_OBSERVER(AppListModelObserver,
354                     observers_,
355                     OnAppListItemUpdated(item));
356   return item;
357 }
358
359 AppListItem* AppListModel::AddItemToFolderItemAndNotify(
360     AppListFolderItem* folder,
361     scoped_ptr<AppListItem> item_ptr) {
362   CHECK_NE(folder->id(), item_ptr->folder_id());
363   AppListItem* item = folder->item_list()->AddItem(item_ptr.Pass());
364   item->set_folder_id(folder->id());
365   FOR_EACH_OBSERVER(AppListModelObserver,
366                     observers_,
367                     OnAppListItemUpdated(item));
368   return item;
369 }
370
371 scoped_ptr<AppListItem> AppListModel::RemoveItem(AppListItem* item) {
372   if (!item->IsInFolder())
373     return top_level_item_list_->RemoveItem(item->id());
374
375   AppListFolderItem* folder = FindFolderItem(item->folder_id());
376   return RemoveItemFromFolder(folder, item);
377 }
378
379 scoped_ptr<AppListItem> AppListModel::RemoveItemFromFolder(
380     AppListFolderItem* folder,
381     AppListItem* item) {
382   std::string folder_id = folder->id();
383   CHECK_EQ(item->folder_id(), folder_id);
384   scoped_ptr<AppListItem> result = folder->item_list()->RemoveItem(item->id());
385   result->set_folder_id("");
386   if (folder->item_list()->item_count() == 0) {
387     DVLOG(2) << "Deleting empty folder: " << folder->ToDebugString();
388     DeleteItem(folder_id);
389   }
390   return result.Pass();
391 }
392
393 }  // namespace app_list