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