// 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 "ui/base/models/simple_menu_model.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/image/image.h" namespace ui { const int kSeparatorId = -1; struct SimpleMenuModel::Item { int command_id; base::string16 label; base::string16 sublabel; base::string16 minor_text; gfx::Image icon; ItemType type; int group_id; MenuModel* submenu; ButtonMenuItemModel* button_model; MenuSeparatorType separator_type; }; //////////////////////////////////////////////////////////////////////////////// // SimpleMenuModel::Delegate, public: bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const { return true; } bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic( int command_id) const { return false; } base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId( int command_id) const { return base::string16(); } base::string16 SimpleMenuModel::Delegate::GetSublabelForCommandId( int command_id) const { return base::string16(); } base::string16 SimpleMenuModel::Delegate::GetMinorTextForCommandId( int command_id) const { return base::string16(); } bool SimpleMenuModel::Delegate::GetIconForCommandId( int command_id, gfx::Image* image_skia) const { return false; } void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) { } void SimpleMenuModel::Delegate::ExecuteCommand( int command_id, int event_flags) { ExecuteCommand(command_id, event_flags); } void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) { } void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) { } //////////////////////////////////////////////////////////////////////////////// // SimpleMenuModel, public: SimpleMenuModel::SimpleMenuModel(Delegate* delegate) : delegate_(delegate), menu_model_delegate_(NULL), method_factory_(this) { } SimpleMenuModel::~SimpleMenuModel() { } void SimpleMenuModel::AddItem(int command_id, const base::string16& label) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_COMMAND, -1, NULL, NULL, NORMAL_SEPARATOR }; AppendItem(item); } void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) { AddItem(command_id, l10n_util::GetStringUTF16(string_id)); } void SimpleMenuModel::AddCheckItem(int command_id, const base::string16& label) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_CHECK, -1, NULL, NULL, NORMAL_SEPARATOR }; AppendItem(item); } void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) { AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id)); } void SimpleMenuModel::AddRadioItem(int command_id, const base::string16& label, int group_id) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_RADIO, group_id, NULL, NULL, NORMAL_SEPARATOR }; AppendItem(item); } void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id, int group_id) { AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id); } void SimpleMenuModel::AddSeparator(MenuSeparatorType separator_type) { if (items_.empty()) { if (separator_type == NORMAL_SEPARATOR) { return; } DCHECK_EQ(SPACING_SEPARATOR, separator_type); } else if (items_.back().type == TYPE_SEPARATOR) { DCHECK_EQ(NORMAL_SEPARATOR, separator_type); DCHECK_EQ(NORMAL_SEPARATOR, items_.back().separator_type); return; } #if !defined(USE_AURA) if (separator_type != NORMAL_SEPARATOR) NOTIMPLEMENTED(); #endif Item item = { kSeparatorId, base::string16(), base::string16(), base::string16(), gfx::Image(), TYPE_SEPARATOR, -1, NULL, NULL, separator_type }; AppendItem(item); } void SimpleMenuModel::RemoveTrailingSeparators() { while (!items_.empty() && items_.back().type == TYPE_SEPARATOR) items_.pop_back(); MenuItemsChanged(); } void SimpleMenuModel::AddButtonItem(int command_id, ButtonMenuItemModel* model) { Item item = { command_id, base::string16(), base::string16(), base::string16(), gfx::Image(), TYPE_BUTTON_ITEM, -1, NULL, model, NORMAL_SEPARATOR }; AppendItem(item); } void SimpleMenuModel::AddSubMenu(int command_id, const base::string16& label, MenuModel* model) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_SUBMENU, -1, model, NULL, NORMAL_SEPARATOR }; AppendItem(item); } void SimpleMenuModel::AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model) { AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); } void SimpleMenuModel::InsertItemAt(int index, int command_id, const base::string16& label) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_COMMAND, -1, NULL, NULL, NORMAL_SEPARATOR }; InsertItemAtIndex(item, index); } void SimpleMenuModel::InsertItemWithStringIdAt( int index, int command_id, int string_id) { InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); } void SimpleMenuModel::InsertSeparatorAt(int index, MenuSeparatorType separator_type) { #if !defined(USE_AURA) if (separator_type != NORMAL_SEPARATOR) { NOTIMPLEMENTED(); } #endif Item item = { kSeparatorId, base::string16(), base::string16(), base::string16(), gfx::Image(), TYPE_SEPARATOR, -1, NULL, NULL, separator_type }; InsertItemAtIndex(item, index); } void SimpleMenuModel::InsertCheckItemAt(int index, int command_id, const base::string16& label) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_CHECK, -1, NULL, NULL, NORMAL_SEPARATOR }; InsertItemAtIndex(item, index); } void SimpleMenuModel::InsertCheckItemWithStringIdAt( int index, int command_id, int string_id) { InsertCheckItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); } void SimpleMenuModel::InsertRadioItemAt(int index, int command_id, const base::string16& label, int group_id) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_RADIO, group_id, NULL, NULL, NORMAL_SEPARATOR }; InsertItemAtIndex(item, index); } void SimpleMenuModel::InsertRadioItemWithStringIdAt( int index, int command_id, int string_id, int group_id) { InsertRadioItemAt( index, command_id, l10n_util::GetStringUTF16(string_id), group_id); } void SimpleMenuModel::InsertSubMenuAt(int index, int command_id, const base::string16& label, MenuModel* model) { Item item = { command_id, label, base::string16(), base::string16(), gfx::Image(), TYPE_SUBMENU, -1, model, NULL, NORMAL_SEPARATOR }; InsertItemAtIndex(item, index); } void SimpleMenuModel::InsertSubMenuWithStringIdAt( int index, int command_id, int string_id, MenuModel* model) { InsertSubMenuAt(index, command_id, l10n_util::GetStringUTF16(string_id), model); } void SimpleMenuModel::RemoveItemAt(int index) { items_.erase(items_.begin() + ValidateItemIndex(index)); MenuItemsChanged(); } void SimpleMenuModel::SetIcon(int index, const gfx::Image& icon) { items_[ValidateItemIndex(index)].icon = icon; MenuItemsChanged(); } void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) { items_[ValidateItemIndex(index)].sublabel = sublabel; MenuItemsChanged(); } void SimpleMenuModel::SetMinorText(int index, const base::string16& minor_text) { items_[ValidateItemIndex(index)].minor_text = minor_text; } void SimpleMenuModel::Clear() { items_.clear(); MenuItemsChanged(); } int SimpleMenuModel::GetIndexOfCommandId(int command_id) { for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) { if (i->command_id == command_id) return static_cast<int>(std::distance(items_.begin(), i)); } return -1; } //////////////////////////////////////////////////////////////////////////////// // SimpleMenuModel, MenuModel implementation: bool SimpleMenuModel::HasIcons() const { for (ItemVector::const_iterator i = items_.begin(); i != items_.end(); ++i) { if (!i->icon.IsEmpty()) return true; } return false; } int SimpleMenuModel::GetItemCount() const { return static_cast<int>(items_.size()); } MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const { return items_[ValidateItemIndex(index)].type; } ui::MenuSeparatorType SimpleMenuModel::GetSeparatorTypeAt(int index) const { return items_[ValidateItemIndex(index)].separator_type; } int SimpleMenuModel::GetCommandIdAt(int index) const { return items_[ValidateItemIndex(index)].command_id; } base::string16 SimpleMenuModel::GetLabelAt(int index) const { if (IsItemDynamicAt(index)) return delegate_->GetLabelForCommandId(GetCommandIdAt(index)); return items_[ValidateItemIndex(index)].label; } base::string16 SimpleMenuModel::GetSublabelAt(int index) const { if (IsItemDynamicAt(index)) return delegate_->GetSublabelForCommandId(GetCommandIdAt(index)); return items_[ValidateItemIndex(index)].sublabel; } base::string16 SimpleMenuModel::GetMinorTextAt(int index) const { if (IsItemDynamicAt(index)) return delegate_->GetMinorTextForCommandId(GetCommandIdAt(index)); return items_[ValidateItemIndex(index)].minor_text; } bool SimpleMenuModel::IsItemDynamicAt(int index) const { if (delegate_) return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index)); return false; } bool SimpleMenuModel::GetAcceleratorAt(int index, ui::Accelerator* accelerator) const { if (delegate_) { return delegate_->GetAcceleratorForCommandId(GetCommandIdAt(index), accelerator); } return false; } bool SimpleMenuModel::IsItemCheckedAt(int index) const { if (!delegate_) return false; MenuModel::ItemType item_type = GetTypeAt(index); return (item_type == TYPE_CHECK || item_type == TYPE_RADIO) ? delegate_->IsCommandIdChecked(GetCommandIdAt(index)) : false; } int SimpleMenuModel::GetGroupIdAt(int index) const { return items_[ValidateItemIndex(index)].group_id; } bool SimpleMenuModel::GetIconAt(int index, gfx::Image* icon) { if (IsItemDynamicAt(index)) return delegate_->GetIconForCommandId(GetCommandIdAt(index), icon); ValidateItemIndex(index); if (items_[index].icon.IsEmpty()) return false; *icon = items_[index].icon; return true; } ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const { return items_[ValidateItemIndex(index)].button_model; } bool SimpleMenuModel::IsEnabledAt(int index) const { int command_id = GetCommandIdAt(index); if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index)) return true; return delegate_->IsCommandIdEnabled(command_id); } bool SimpleMenuModel::IsVisibleAt(int index) const { int command_id = GetCommandIdAt(index); if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index)) return true; return delegate_->IsCommandIdVisible(command_id); } void SimpleMenuModel::HighlightChangedTo(int index) { if (delegate_) delegate_->CommandIdHighlighted(GetCommandIdAt(index)); } void SimpleMenuModel::ActivatedAt(int index) { if (delegate_) delegate_->ExecuteCommand(GetCommandIdAt(index), 0); } void SimpleMenuModel::ActivatedAt(int index, int event_flags) { if (delegate_) delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags); } MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const { return items_[ValidateItemIndex(index)].submenu; } void SimpleMenuModel::MenuWillShow() { if (delegate_) delegate_->MenuWillShow(this); } void SimpleMenuModel::MenuClosed() { // Due to how menus work on the different platforms, ActivatedAt will be // called after this. It's more convenient for the delegate to be called // afterwards though, so post a task. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&SimpleMenuModel::OnMenuClosed, method_factory_.GetWeakPtr())); } void SimpleMenuModel::SetMenuModelDelegate( ui::MenuModelDelegate* menu_model_delegate) { menu_model_delegate_ = menu_model_delegate; } MenuModelDelegate* SimpleMenuModel::GetMenuModelDelegate() const { return menu_model_delegate_; } void SimpleMenuModel::OnMenuClosed() { if (delegate_) delegate_->MenuClosed(this); } //////////////////////////////////////////////////////////////////////////////// // SimpleMenuModel, Protected: void SimpleMenuModel::MenuItemsChanged() { } //////////////////////////////////////////////////////////////////////////////// // SimpleMenuModel, Private: int SimpleMenuModel::ValidateItemIndex(int index) const { CHECK_GE(index, 0); CHECK_LT(static_cast<size_t>(index), items_.size()); return index; } void SimpleMenuModel::AppendItem(const Item& item) { ValidateItem(item); items_.push_back(item); MenuItemsChanged(); } void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) { ValidateItem(item); items_.insert(items_.begin() + index, item); MenuItemsChanged(); } void SimpleMenuModel::ValidateItem(const Item& item) { #ifndef NDEBUG if (item.type == TYPE_SEPARATOR) { DCHECK_EQ(item.command_id, kSeparatorId); } else { DCHECK_GE(item.command_id, 0); } #endif // NDEBUG } } // namespace ui