// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/app_list/app_context_menu.h"
#include <memory>
#include <utility>
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/app_menu_constants.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/apps/app_service/menu_util.h"
#include "chrome/browser/ash/app_list/app_context_menu_delegate.h"
#include "chrome/browser/ash/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h"
#include "chrome/browser/ash/app_list/chrome_app_list_model_updater.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/color/color_id.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/vector_icons.h"
namespace {
void RequestAppListSort(Profile* profile, ash::AppListSortOrder order) {
ChromeAppListModelUpdater* model_updater =
static_cast<ChromeAppListModelUpdater*>(
app_list::AppListSyncableServiceFactory::GetForProfile(profile)
->GetModelUpdater());
model_updater->RequestAppListSort(order);
}
} // namespace
namespace app_list {
AppContextMenu::AppContextMenu(AppContextMenuDelegate* delegate,
Profile* profile,
const std::string& app_id,
AppListControllerDelegate* controller,
ash::AppListItemContext item_context)
: delegate_(delegate),
profile_(profile),
app_id_(app_id),
controller_(controller),
item_context_(item_context) {}
AppContextMenu::~AppContextMenu() = default;
void AppContextMenu::GetMenuModel(GetMenuModelCallback callback) {
auto menu_model = std::make_unique<ui::SimpleMenuModel>(this);
BuildMenu(menu_model.get());
std::move(callback).Run(std::move(menu_model));
}
bool AppContextMenu::IsItemForCommandIdDynamic(int command_id) const {
return command_id == ash::TOGGLE_PIN;
}
std::u16string AppContextMenu::GetLabelForCommandId(int command_id) const {
DCHECK_EQ(command_id, ash::TOGGLE_PIN);
// Return "{Pin to, Unpin from} shelf" or "Pinned by administrator".
// Note this only exists on Ash desktops.
if (controller_->GetPinnable(app_id()) ==
AppListControllerDelegate::PIN_FIXED) {
return l10n_util::GetStringUTF16(
IDS_APP_LIST_CONTEXT_MENU_PIN_ENFORCED_BY_POLICY);
}
return controller_->IsAppPinned(app_id_)
? l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_UNPIN)
: l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_PIN);
}
bool AppContextMenu::IsCommandIdEnabled(int command_id) const {
if (command_id == ash::TOGGLE_PIN) {
return controller_->GetPinnable(app_id_) ==
AppListControllerDelegate::PIN_EDITABLE;
}
return true;
}
void AppContextMenu::ExecuteCommand(int command_id, int event_flags) {
switch (command_id) {
case ash::TOGGLE_PIN:
TogglePin(app_id_);
break;
case ash::REORDER_BY_NAME_ALPHABETICAL:
RequestAppListSort(profile(), ash::AppListSortOrder::kNameAlphabetical);
break;
case ash::REORDER_BY_NAME_REVERSE_ALPHABETICAL:
RequestAppListSort(profile(),
ash::AppListSortOrder::kNameReverseAlphabetical);
break;
case ash::REORDER_BY_COLOR:
RequestAppListSort(profile(), ash::AppListSortOrder::kColor);
break;
}
}
ui::ImageModel AppContextMenu::GetIconForCommandId(int command_id) const {
DCHECK_EQ(command_id, ash::TOGGLE_PIN);
const gfx::VectorIcon& icon =
GetMenuItemVectorIcon(command_id, controller_->IsAppPinned(app_id_)
? IDS_APP_LIST_CONTEXT_MENU_UNPIN
: IDS_APP_LIST_CONTEXT_MENU_PIN);
return ui::ImageModel::FromVectorIcon(icon, apps::GetColorIdForMenuItemIcon(),
ash::kAppContextMenuIconSize);
}
// static
const gfx::VectorIcon& AppContextMenu::GetMenuItemVectorIcon(int command_id,
int string_id) {
switch (command_id) {
case ash::LAUNCH_NEW:
if (string_id == IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW)
return views::kNewWindowIcon;
if (string_id == IDS_APP_LIST_CONTEXT_MENU_NEW_TAB)
return views::kNewTabIcon;
// The LAUNCH_NEW command is for an ARC app.
return views::kLaunchIcon;
case ash::TOGGLE_PIN:
return string_id == IDS_APP_LIST_CONTEXT_MENU_PIN ? views::kPinIcon
: views::kUnpinIcon;
case ash::SHOW_APP_INFO:
return views::kInfoIcon;
case ash::OPTIONS:
return views::kOptionsIcon;
case ash::UNINSTALL:
return views::kUninstallIcon;
case ash::APP_CONTEXT_MENU_NEW_WINDOW:
return views::kNewWindowIcon;
case ash::APP_CONTEXT_MENU_NEW_INCOGNITO_WINDOW:
return views::kNewIncognitoWindowIcon;
case ash::INSTALL:
// Deprecated.
return gfx::kNoneIcon;
case ash::SETTINGS:
return vector_icons::kSettingsIcon;
case ash::USE_LAUNCH_TYPE_REGULAR:
case ash::USE_LAUNCH_TYPE_WINDOW:
case ash::USE_LAUNCH_TYPE_TABBED_WINDOW:
// Check items use the default icon.
return gfx::kNoneIcon;
case ash::DEPRECATED_USE_LAUNCH_TYPE_PINNED:
case ash::DEPRECATED_USE_LAUNCH_TYPE_FULLSCREEN:
NOTREACHED_IN_MIGRATION();
return gfx::kNoneIcon;
case ash::REORDER_SUBMENU:
return ash::kReorderIcon;
case ash::REORDER_BY_NAME_ALPHABETICAL:
return ash::kSortAlphabeticalIcon;
case ash::REORDER_BY_COLOR:
return ash::kSortColorIcon;
case ash::NOTIFICATION_CONTAINER:
NOTREACHED_IN_MIGRATION()
<< "NOTIFICATION_CONTAINER does not have an icon, and it is "
"added to the model by NotificationMenuController.";
return gfx::kNoneIcon;
case ash::SHUTDOWN_GUEST_OS:
return kShutdownGuestOsIcon;
default:
NOTREACHED_IN_MIGRATION() << "No icon for command_id: " << command_id;
return gfx::kNoneIcon;
}
}
void AppContextMenu::BuildMenu(ui::SimpleMenuModel* menu_model) {
// Show Pin/Unpin option if shelf is available.
if (controller_->GetPinnable(app_id()) != AppListControllerDelegate::NO_PIN) {
AddContextMenuOption(menu_model, ash::TOGGLE_PIN,
controller_->IsAppPinned(app_id_)
? IDS_APP_LIST_CONTEXT_MENU_UNPIN
: IDS_APP_LIST_CONTEXT_MENU_PIN);
}
}
void AppContextMenu::TogglePin(const std::string& shelf_app_id) {
DCHECK_EQ(AppListControllerDelegate::PIN_EDITABLE,
controller_->GetPinnable(shelf_app_id));
ash::ShelfModel::ScopedUserTriggeredMutation user_triggered(
ash::ShelfModel::Get());
if (controller_->IsAppPinned(shelf_app_id))
controller_->UnpinApp(shelf_app_id);
else
controller_->PinApp(shelf_app_id);
}
void AppContextMenu::AddContextMenuOption(ui::SimpleMenuModel* menu_model,
ash::CommandId command_id,
int string_id) {
// Do not include disabled items.
if (!IsCommandIdEnabled(command_id))
return;
const gfx::VectorIcon& icon = GetMenuItemVectorIcon(command_id, string_id);
if (!icon.is_empty()) {
menu_model->AddItemWithStringIdAndIcon(
command_id, string_id,
ui::ImageModel::FromVectorIcon(icon, apps::GetColorIdForMenuItemIcon(),
ash::kAppContextMenuIconSize));
return;
}
// Check items use default icons.
if (command_id >= ash::USE_LAUNCH_TYPE_COMMAND_START &&
command_id < ash::USE_LAUNCH_TYPE_COMMAND_END) {
menu_model->AddCheckItemWithStringId(command_id, string_id);
return;
}
if (command_id == ash::NOTIFICATION_CONTAINER) {
NOTREACHED_IN_MIGRATION()
<< "NOTIFICATION_CONTAINER is added by NotificationMenuController.";
return;
}
menu_model->AddItemWithStringId(command_id, string_id);
}
void AppContextMenu::AddReorderMenuOption(ui::SimpleMenuModel* menu_model) {
if (item_context_ != ash::AppListItemContext::kAppsGrid &&
item_context_ != ash::AppListItemContext::kAppsCollectionsGrid) {
return;
}
const ui::ColorId color_id = apps::GetColorIdForMenuItemIcon();
reorder_submenu_ = std::make_unique<ui::SimpleMenuModel>(this);
// As all the options below are only for tests and are expected to change in
// the future, the strings are directly written as the parameters.
reorder_submenu_->AddItemWithIcon(
ash::REORDER_BY_NAME_ALPHABETICAL,
l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_BY_NAME),
ui::ImageModel::FromVectorIcon(
GetMenuItemVectorIcon(ash::REORDER_BY_NAME_ALPHABETICAL,
/*string_id=*/-1),
color_id));
reorder_submenu_->AddItemWithIcon(
ash::REORDER_BY_COLOR,
l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_BY_COLOR),
ui::ImageModel::FromVectorIcon(
GetMenuItemVectorIcon(ash::REORDER_BY_COLOR, /*string_id=*/-1),
color_id));
menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
menu_model->AddSubMenuWithIcon(
ash::REORDER_SUBMENU,
l10n_util::GetStringUTF16(IDS_APP_LIST_CONTEXT_MENU_REORDER_TITLE),
reorder_submenu_.get(),
ui::ImageModel::FromVectorIcon(
GetMenuItemVectorIcon(ash::REORDER_SUBMENU, /*string_id=*/-1),
color_id));
}
} // namespace app_list