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/base/models/simple_menu_model.h" 6 7#include "base/bind.h" 8#include "base/message_loop/message_loop.h" 9#include "ui/base/l10n/l10n_util.h" 10#include "ui/gfx/image/image.h" 11 12namespace ui { 13 14const int kSeparatorId = -1; 15 16struct SimpleMenuModel::Item { 17 int command_id; 18 base::string16 label; 19 base::string16 sublabel; 20 base::string16 minor_text; 21 gfx::Image icon; 22 ItemType type; 23 int group_id; 24 MenuModel* submenu; 25 ButtonMenuItemModel* button_model; 26 MenuSeparatorType separator_type; 27}; 28 29//////////////////////////////////////////////////////////////////////////////// 30// SimpleMenuModel::Delegate, public: 31 32bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const { 33 return true; 34} 35 36bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic( 37 int command_id) const { 38 return false; 39} 40 41base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId( 42 int command_id) const { 43 return base::string16(); 44} 45 46base::string16 SimpleMenuModel::Delegate::GetSublabelForCommandId( 47 int command_id) const { 48 return base::string16(); 49} 50 51base::string16 SimpleMenuModel::Delegate::GetMinorTextForCommandId( 52 int command_id) const { 53 return base::string16(); 54} 55 56bool SimpleMenuModel::Delegate::GetIconForCommandId( 57 int command_id, gfx::Image* image_skia) const { 58 return false; 59} 60 61void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) { 62} 63 64void SimpleMenuModel::Delegate::ExecuteCommand( 65 int command_id, int event_flags) { 66 ExecuteCommand(command_id, event_flags); 67} 68 69void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) { 70} 71 72void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) { 73} 74 75//////////////////////////////////////////////////////////////////////////////// 76// SimpleMenuModel, public: 77 78SimpleMenuModel::SimpleMenuModel(Delegate* delegate) 79 : delegate_(delegate), 80 menu_model_delegate_(NULL), 81 method_factory_(this) { 82} 83 84SimpleMenuModel::~SimpleMenuModel() { 85} 86 87void SimpleMenuModel::AddItem(int command_id, const base::string16& label) { 88 Item item = { command_id, label, base::string16(), base::string16(), 89 gfx::Image(), TYPE_COMMAND, -1, NULL, NULL, NORMAL_SEPARATOR }; 90 AppendItem(item); 91} 92 93void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) { 94 AddItem(command_id, l10n_util::GetStringUTF16(string_id)); 95} 96 97void SimpleMenuModel::AddCheckItem(int command_id, 98 const base::string16& label) { 99 Item item = { command_id, label, base::string16(), base::string16(), 100 gfx::Image(), TYPE_CHECK, -1, NULL, NULL, NORMAL_SEPARATOR }; 101 AppendItem(item); 102} 103 104void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) { 105 AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id)); 106} 107 108void SimpleMenuModel::AddRadioItem(int command_id, 109 const base::string16& label, 110 int group_id) { 111 Item item = { command_id, label, base::string16(), base::string16(), 112 gfx::Image(), TYPE_RADIO, group_id, NULL, NULL, 113 NORMAL_SEPARATOR }; 114 AppendItem(item); 115} 116 117void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id, 118 int group_id) { 119 AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id); 120} 121 122void SimpleMenuModel::AddSeparator(MenuSeparatorType separator_type) { 123 if (items_.empty()) { 124 if (separator_type == NORMAL_SEPARATOR) { 125 return; 126 } 127 DCHECK_EQ(SPACING_SEPARATOR, separator_type); 128 } else if (items_.back().type == TYPE_SEPARATOR) { 129 DCHECK_EQ(NORMAL_SEPARATOR, separator_type); 130 DCHECK_EQ(NORMAL_SEPARATOR, items_.back().separator_type); 131 return; 132 } 133#if !defined(USE_AURA) 134 if (separator_type != NORMAL_SEPARATOR) 135 NOTIMPLEMENTED(); 136#endif 137 Item item = { kSeparatorId, base::string16(), base::string16(), 138 base::string16(), gfx::Image(), TYPE_SEPARATOR, -1, NULL, NULL, 139 separator_type }; 140 AppendItem(item); 141} 142 143void SimpleMenuModel::RemoveTrailingSeparators() { 144 while (!items_.empty() && items_.back().type == TYPE_SEPARATOR) 145 items_.pop_back(); 146 MenuItemsChanged(); 147} 148 149void SimpleMenuModel::AddButtonItem(int command_id, 150 ButtonMenuItemModel* model) { 151 Item item = { command_id, base::string16(), base::string16(), 152 base::string16(), gfx::Image(), TYPE_BUTTON_ITEM, -1, NULL, 153 model, NORMAL_SEPARATOR }; 154 AppendItem(item); 155} 156 157void SimpleMenuModel::AddSubMenu(int command_id, 158 const base::string16& label, 159 MenuModel* model) { 160 Item item = { command_id, label, base::string16(), base::string16(), 161 gfx::Image(), TYPE_SUBMENU, -1, model, NULL, NORMAL_SEPARATOR }; 162 AppendItem(item); 163} 164 165void SimpleMenuModel::AddSubMenuWithStringId(int command_id, 166 int string_id, MenuModel* model) { 167 AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); 168} 169 170void SimpleMenuModel::InsertItemAt(int index, 171 int command_id, 172 const base::string16& label) { 173 Item item = { command_id, label, base::string16(), base::string16(), 174 gfx::Image(), TYPE_COMMAND, -1, NULL, NULL, NORMAL_SEPARATOR }; 175 InsertItemAtIndex(item, index); 176} 177 178void SimpleMenuModel::InsertItemWithStringIdAt( 179 int index, int command_id, int string_id) { 180 InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); 181} 182 183void SimpleMenuModel::InsertSeparatorAt(int index, 184 MenuSeparatorType separator_type) { 185#if !defined(USE_AURA) 186 if (separator_type != NORMAL_SEPARATOR) { 187 NOTIMPLEMENTED(); 188 } 189#endif 190 Item item = { kSeparatorId, base::string16(), base::string16(), 191 base::string16(), gfx::Image(), TYPE_SEPARATOR, -1, NULL, NULL, 192 separator_type }; 193 InsertItemAtIndex(item, index); 194} 195 196void SimpleMenuModel::InsertCheckItemAt(int index, 197 int command_id, 198 const base::string16& label) { 199 Item item = { command_id, label, base::string16(), base::string16(), 200 gfx::Image(), TYPE_CHECK, -1, NULL, NULL, NORMAL_SEPARATOR }; 201 InsertItemAtIndex(item, index); 202} 203 204void SimpleMenuModel::InsertCheckItemWithStringIdAt( 205 int index, int command_id, int string_id) { 206 InsertCheckItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); 207} 208 209void SimpleMenuModel::InsertRadioItemAt(int index, 210 int command_id, 211 const base::string16& label, 212 int group_id) { 213 Item item = { command_id, label, base::string16(), base::string16(), 214 gfx::Image(), TYPE_RADIO, group_id, NULL, NULL, 215 NORMAL_SEPARATOR }; 216 InsertItemAtIndex(item, index); 217} 218 219void SimpleMenuModel::InsertRadioItemWithStringIdAt( 220 int index, int command_id, int string_id, int group_id) { 221 InsertRadioItemAt( 222 index, command_id, l10n_util::GetStringUTF16(string_id), group_id); 223} 224 225void SimpleMenuModel::InsertSubMenuAt(int index, 226 int command_id, 227 const base::string16& label, 228 MenuModel* model) { 229 Item item = { command_id, label, base::string16(), base::string16(), 230 gfx::Image(), TYPE_SUBMENU, -1, model, NULL, 231 NORMAL_SEPARATOR }; 232 InsertItemAtIndex(item, index); 233} 234 235void SimpleMenuModel::InsertSubMenuWithStringIdAt( 236 int index, int command_id, int string_id, MenuModel* model) { 237 InsertSubMenuAt(index, command_id, l10n_util::GetStringUTF16(string_id), 238 model); 239} 240 241void SimpleMenuModel::RemoveItemAt(int index) { 242 items_.erase(items_.begin() + ValidateItemIndex(index)); 243 MenuItemsChanged(); 244} 245 246void SimpleMenuModel::SetIcon(int index, const gfx::Image& icon) { 247 items_[ValidateItemIndex(index)].icon = icon; 248 MenuItemsChanged(); 249} 250 251void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) { 252 items_[ValidateItemIndex(index)].sublabel = sublabel; 253 MenuItemsChanged(); 254} 255 256void SimpleMenuModel::SetMinorText(int index, 257 const base::string16& minor_text) { 258 items_[ValidateItemIndex(index)].minor_text = minor_text; 259} 260 261void SimpleMenuModel::Clear() { 262 items_.clear(); 263 MenuItemsChanged(); 264} 265 266int SimpleMenuModel::GetIndexOfCommandId(int command_id) { 267 for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) { 268 if (i->command_id == command_id) 269 return static_cast<int>(std::distance(items_.begin(), i)); 270 } 271 return -1; 272} 273 274//////////////////////////////////////////////////////////////////////////////// 275// SimpleMenuModel, MenuModel implementation: 276 277bool SimpleMenuModel::HasIcons() const { 278 for (ItemVector::const_iterator i = items_.begin(); i != items_.end(); ++i) { 279 if (!i->icon.IsEmpty()) 280 return true; 281 } 282 283 return false; 284} 285 286int SimpleMenuModel::GetItemCount() const { 287 return static_cast<int>(items_.size()); 288} 289 290MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const { 291 return items_[ValidateItemIndex(index)].type; 292} 293 294ui::MenuSeparatorType SimpleMenuModel::GetSeparatorTypeAt(int index) const { 295 return items_[ValidateItemIndex(index)].separator_type; 296} 297 298int SimpleMenuModel::GetCommandIdAt(int index) const { 299 return items_[ValidateItemIndex(index)].command_id; 300} 301 302base::string16 SimpleMenuModel::GetLabelAt(int index) const { 303 if (IsItemDynamicAt(index)) 304 return delegate_->GetLabelForCommandId(GetCommandIdAt(index)); 305 return items_[ValidateItemIndex(index)].label; 306} 307 308base::string16 SimpleMenuModel::GetSublabelAt(int index) const { 309 if (IsItemDynamicAt(index)) 310 return delegate_->GetSublabelForCommandId(GetCommandIdAt(index)); 311 return items_[ValidateItemIndex(index)].sublabel; 312} 313 314base::string16 SimpleMenuModel::GetMinorTextAt(int index) const { 315 if (IsItemDynamicAt(index)) 316 return delegate_->GetMinorTextForCommandId(GetCommandIdAt(index)); 317 return items_[ValidateItemIndex(index)].minor_text; 318} 319 320bool SimpleMenuModel::IsItemDynamicAt(int index) const { 321 if (delegate_) 322 return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index)); 323 return false; 324} 325 326bool SimpleMenuModel::GetAcceleratorAt(int index, 327 ui::Accelerator* accelerator) const { 328 if (delegate_) { 329 return delegate_->GetAcceleratorForCommandId(GetCommandIdAt(index), 330 accelerator); 331 } 332 return false; 333} 334 335bool SimpleMenuModel::IsItemCheckedAt(int index) const { 336 if (!delegate_) 337 return false; 338 MenuModel::ItemType item_type = GetTypeAt(index); 339 return (item_type == TYPE_CHECK || item_type == TYPE_RADIO) ? 340 delegate_->IsCommandIdChecked(GetCommandIdAt(index)) : false; 341} 342 343int SimpleMenuModel::GetGroupIdAt(int index) const { 344 return items_[ValidateItemIndex(index)].group_id; 345} 346 347bool SimpleMenuModel::GetIconAt(int index, gfx::Image* icon) { 348 if (IsItemDynamicAt(index)) 349 return delegate_->GetIconForCommandId(GetCommandIdAt(index), icon); 350 351 ValidateItemIndex(index); 352 if (items_[index].icon.IsEmpty()) 353 return false; 354 355 *icon = items_[index].icon; 356 return true; 357} 358 359ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const { 360 return items_[ValidateItemIndex(index)].button_model; 361} 362 363bool SimpleMenuModel::IsEnabledAt(int index) const { 364 int command_id = GetCommandIdAt(index); 365 if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index)) 366 return true; 367 return delegate_->IsCommandIdEnabled(command_id); 368} 369 370bool SimpleMenuModel::IsVisibleAt(int index) const { 371 int command_id = GetCommandIdAt(index); 372 if (!delegate_ || command_id == kSeparatorId || GetButtonMenuItemAt(index)) 373 return true; 374 return delegate_->IsCommandIdVisible(command_id); 375} 376 377void SimpleMenuModel::HighlightChangedTo(int index) { 378 if (delegate_) 379 delegate_->CommandIdHighlighted(GetCommandIdAt(index)); 380} 381 382void SimpleMenuModel::ActivatedAt(int index) { 383 if (delegate_) 384 delegate_->ExecuteCommand(GetCommandIdAt(index), 0); 385} 386 387void SimpleMenuModel::ActivatedAt(int index, int event_flags) { 388 if (delegate_) 389 delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags); 390} 391 392MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const { 393 return items_[ValidateItemIndex(index)].submenu; 394} 395 396void SimpleMenuModel::MenuWillShow() { 397 if (delegate_) 398 delegate_->MenuWillShow(this); 399} 400 401void SimpleMenuModel::MenuClosed() { 402 // Due to how menus work on the different platforms, ActivatedAt will be 403 // called after this. It's more convenient for the delegate to be called 404 // afterwards though, so post a task. 405 base::MessageLoop::current()->PostTask( 406 FROM_HERE, 407 base::Bind(&SimpleMenuModel::OnMenuClosed, method_factory_.GetWeakPtr())); 408} 409 410void SimpleMenuModel::SetMenuModelDelegate( 411 ui::MenuModelDelegate* menu_model_delegate) { 412 menu_model_delegate_ = menu_model_delegate; 413} 414 415MenuModelDelegate* SimpleMenuModel::GetMenuModelDelegate() const { 416 return menu_model_delegate_; 417} 418 419void SimpleMenuModel::OnMenuClosed() { 420 if (delegate_) 421 delegate_->MenuClosed(this); 422} 423 424//////////////////////////////////////////////////////////////////////////////// 425// SimpleMenuModel, Protected: 426 427void SimpleMenuModel::MenuItemsChanged() { 428} 429 430//////////////////////////////////////////////////////////////////////////////// 431// SimpleMenuModel, Private: 432 433int SimpleMenuModel::ValidateItemIndex(int index) const { 434 CHECK_GE(index, 0); 435 CHECK_LT(static_cast<size_t>(index), items_.size()); 436 return index; 437} 438 439void SimpleMenuModel::AppendItem(const Item& item) { 440 ValidateItem(item); 441 items_.push_back(item); 442 MenuItemsChanged(); 443} 444 445void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) { 446 ValidateItem(item); 447 items_.insert(items_.begin() + index, item); 448 MenuItemsChanged(); 449} 450 451void SimpleMenuModel::ValidateItem(const Item& item) { 452#ifndef NDEBUG 453 if (item.type == TYPE_SEPARATOR) { 454 DCHECK_EQ(item.command_id, kSeparatorId); 455 } else { 456 DCHECK_GE(item.command_id, 0); 457 } 458#endif // NDEBUG 459} 460 461} // namespace ui 462