Index: chrome/browser/ui/ash/app_list/apps_model_builder.cc |
diff --git a/chrome/browser/ui/ash/app_list/apps_model_builder.cc b/chrome/browser/ui/ash/app_list/apps_model_builder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..47a97e85e51e40e2f66c5b1dc00f16e01470be23 |
--- /dev/null |
+++ b/chrome/browser/ui/ash/app_list/apps_model_builder.cc |
@@ -0,0 +1,226 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/ui/ash/app_list/apps_model_builder.h" |
+ |
+#include "base/i18n/case_conversion.h" |
+#include "base/utf_string_conversions.h" |
+#include "chrome/browser/browser_process.h" |
+#include "chrome/browser/extensions/extension_service.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/ui/ash/app_list/extension_app_item.h" |
+#include "chrome/common/chrome_notification_types.h" |
+#include "chrome/common/extensions/extension.h" |
+#include "content/public/browser/notification_service.h" |
+#include "ui/base/l10n/l10n_util_collator.h" |
+ |
+using extensions::Extension; |
+ |
+namespace { |
+ |
+const char* kSpecialApps[] = { |
+ extension_misc::kChromeAppId, |
+ extension_misc::kWebStoreAppId, |
+}; |
+ |
+// ModelItemSortData provides a string key to sort with |
+// l10n_util::StringComparator. |
+struct ModelItemSortData { |
+ explicit ModelItemSortData(app_list::AppListItemModel* app) |
+ : app(app), |
+ key(base::i18n::ToLower(UTF8ToUTF16(app->title()))) { |
+ } |
+ |
+ // Needed by StringComparator<Element> in SortVectorWithStringKey, which uses |
+ // this method to get a key to sort. |
+ const string16& GetStringKey() const { |
+ return key; |
+ } |
+ |
+ app_list::AppListItemModel* app; |
+ string16 key; |
+}; |
+ |
+// Returns true if given |extension_id| is listed in kSpecialApps. |
+bool IsSpecialApp(const std::string& extension_id) { |
+ for (size_t i = 0; i < arraysize(kSpecialApps); ++i) { |
+ if (extension_id == kSpecialApps[i]) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+} // namespace |
+ |
+AppsModelBuilder::AppsModelBuilder(Profile* profile, |
+ app_list::AppListModel::Apps* model) |
+ : profile_(profile), |
+ model_(model), |
+ special_apps_count_(0) { |
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, |
+ content::Source<Profile>(profile_)); |
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, |
+ content::Source<Profile>(profile_)); |
+ registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST, |
+ content::Source<Profile>(profile_)); |
+} |
+ |
+AppsModelBuilder::~AppsModelBuilder() { |
+} |
+ |
+void AppsModelBuilder::Build() { |
+ DCHECK(model_ && model_->item_count() == 0); |
+ CreateSpecialApps(); |
+ |
+ Apps apps; |
+ GetExtensionApps(&apps); |
+ |
+ SortAndPopulateModel(apps); |
+ HighlightApp(); |
+} |
+ |
+void AppsModelBuilder::SortAndPopulateModel(const Apps& apps) { |
+ // Just return if there is nothing to populate. |
+ if (apps.empty()) |
+ return; |
+ |
+ // Sort apps case insensitive alphabetically. |
+ std::vector<ModelItemSortData> sorted; |
+ for (Apps::const_iterator it = apps.begin(); it != apps.end(); ++it) |
+ sorted.push_back(ModelItemSortData(*it)); |
+ |
+ l10n_util::SortVectorWithStringKey(g_browser_process->GetApplicationLocale(), |
+ &sorted, |
+ false /* needs_stable_sort */); |
+ for (std::vector<ModelItemSortData>::const_iterator it = sorted.begin(); |
+ it != sorted.end(); |
+ ++it) { |
+ model_->Add(it->app); |
+ } |
+} |
+ |
+void AppsModelBuilder::InsertItemByTitle(app_list::AppListItemModel* app) { |
+ DCHECK(model_); |
+ |
+ icu::Locale locale(g_browser_process->GetApplicationLocale().c_str()); |
+ UErrorCode error = U_ZERO_ERROR; |
+ scoped_ptr<icu::Collator> collator( |
+ icu::Collator::createInstance(locale, error)); |
+ if (U_FAILURE(error)) |
+ collator.reset(); |
+ |
+ l10n_util::StringComparator<string16> c(collator.get()); |
+ ModelItemSortData data(app); |
+ for (size_t i = special_apps_count_; i < model_->item_count(); ++i) { |
+ ModelItemSortData current(model_->GetItemAt(i)); |
+ if (!c(current.key, data.key)) { |
+ model_->AddAt(i, app); |
+ return; |
+ } |
+ } |
+ model_->Add(app); |
+} |
+ |
+void AppsModelBuilder::GetExtensionApps(Apps* apps) { |
+ DCHECK(profile_); |
+ ExtensionService* service = profile_->GetExtensionService(); |
+ if (!service) |
+ return; |
+ |
+ // Get extension apps. |
+ const ExtensionSet* extensions = service->extensions(); |
+ for (ExtensionSet::const_iterator app = extensions->begin(); |
+ app != extensions->end(); ++app) { |
+ if ((*app)->ShouldDisplayInLauncher() && |
+ !IsSpecialApp((*app)->id())) { |
+ apps->push_back(new ExtensionAppItem(profile_, *app)); |
+ } |
+ } |
+} |
+ |
+void AppsModelBuilder::CreateSpecialApps() { |
+ DCHECK(model_ && model_->item_count() == 0); |
+ |
+ bool is_guest_session = Profile::IsGuestSession(); |
+ ExtensionService* service = profile_->GetExtensionService(); |
+ DCHECK(service); |
+ for (size_t i = 0; i < arraysize(kSpecialApps); ++i) { |
+ const std::string extension_id(kSpecialApps[i]); |
+ if (is_guest_session && extension_id == extension_misc::kWebStoreAppId) |
+ continue; |
+ |
+ const Extension* extension = service->GetInstalledExtension(extension_id); |
+ DCHECK(extension); |
+ |
+ model_->Add(new ExtensionAppItem(profile_, extension)); |
+ } |
+ |
+ special_apps_count_ = model_->item_count(); |
+} |
+ |
+int AppsModelBuilder::FindApp(const std::string& app_id) { |
+ DCHECK(model_); |
+ for (size_t i = special_apps_count_; i < model_->item_count(); ++i) { |
+ ChromeAppListItem* app = |
+ static_cast<ChromeAppListItem*>(model_->GetItemAt(i)); |
+ if (app->type() != ChromeAppListItem::TYPE_APP) |
+ continue; |
+ |
+ ExtensionAppItem* extension_item = static_cast<ExtensionAppItem*>(app); |
+ if (extension_item->extension_id() == app_id) |
+ return i; |
+ } |
+ |
+ return -1; |
+} |
+ |
+void AppsModelBuilder::HighlightApp() { |
+ DCHECK(model_); |
+ if (highlight_app_id_.empty()) |
+ return; |
+ |
+ int index = FindApp(highlight_app_id_); |
+ if (index == -1) |
+ return; |
+ |
+ model_->GetItemAt(index)->SetHighlighted(true); |
+ highlight_app_id_.clear(); |
+} |
+ |
+void AppsModelBuilder::Observe(int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) { |
+ switch (type) { |
+ case chrome::NOTIFICATION_EXTENSION_LOADED: { |
+ const Extension* extension = |
+ content::Details<const Extension>(details).ptr(); |
+ if (!extension->ShouldDisplayInLauncher()) |
+ return; |
+ |
+ if (FindApp(extension->id()) != -1) |
+ return; |
+ |
+ InsertItemByTitle(new ExtensionAppItem(profile_, extension)); |
+ HighlightApp(); |
+ break; |
+ } |
+ case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
+ const Extension* extension = |
+ content::Details<extensions::UnloadedExtensionInfo>( |
+ details)->extension; |
+ int index = FindApp(extension->id()); |
+ if (index >= 0) |
+ model_->DeleteAt(index); |
+ break; |
+ } |
+ case chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST: { |
+ highlight_app_id_ = *content::Details<const std::string>(details).ptr(); |
+ HighlightApp(); |
+ break; |
+ } |
+ default: |
+ NOTREACHED(); |
+ } |
+} |