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 12 namespace ui { 13 14 const int kSeparatorId = -1; 15 16 struct 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 32 bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const { 33 return true; 34 } 35 36 bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic( 37 int command_id) const { 38 return false; 39 } 40 41 base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId( 42 int command_id) const { 43 return base::string16(); 44 } 45 46 base::string16 SimpleMenuModel::Delegate::GetSublabelForCommandId( 47 int command_id) const { 48 return base::string16(); 49 } 50 51 base::string16 SimpleMenuModel::Delegate::GetMinorTextForCommandId( 52 int command_id) const { 53 return base::string16(); 54 } 55 56 bool SimpleMenuModel::Delegate::GetIconForCommandId( 57 int command_id, gfx::Image* image_skia) const { 58 return false; 59 } 60 61 void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) { 62 } 63 64 void SimpleMenuModel::Delegate::ExecuteCommand( 65 int command_id, int event_flags) { 66 ExecuteCommand(command_id, event_flags); 67 } 68 69 void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) { 70 } 71 72 void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) { 73 } 74 75 //////////////////////////////////////////////////////////////////////////////// 76 // SimpleMenuModel, public: 77 78 SimpleMenuModel::SimpleMenuModel(Delegate* delegate) 79 : delegate_(delegate), 80 menu_model_delegate_(NULL), 81 method_factory_(this) { 82 } 83 84 SimpleMenuModel::~SimpleMenuModel() { 85 } 86 87 void 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 93 void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) { 94 AddItem(command_id, l10n_util::GetStringUTF16(string_id)); 95 } 96 97 void 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 104 void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) { 105 AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id)); 106 } 107 108 void 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 117 void 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 122 void 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 143 void SimpleMenuModel::RemoveTrailingSeparators() { 144 while (!items_.empty() && items_.back().type == TYPE_SEPARATOR) 145 items_.pop_back(); 146 MenuItemsChanged(); 147 } 148 149 void 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 157 void 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 165 void SimpleMenuModel::AddSubMenuWithStringId(int command_id, 166 int string_id, MenuModel* model) { 167 AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); 168 } 169 170 void 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 178 void SimpleMenuModel::InsertItemWithStringIdAt( 179 int index, int command_id, int string_id) { 180 InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); 181 } 182 183 void 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 196 void 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 204 void SimpleMenuModel::InsertCheckItemWithStringIdAt( 205 int index, int command_id, int string_id) { 206 InsertCheckItemAt(index, command_id, l10n_util::GetStringUTF16(string_id)); 207 } 208 209 void 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 219 void 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 225 void 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 235 void 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 241 void SimpleMenuModel::RemoveItemAt(int index) { 242 items_.erase(items_.begin() + ValidateItemIndex(index)); 243 MenuItemsChanged(); 244 } 245 246 void SimpleMenuModel::SetIcon(int index, const gfx::Image& icon) { 247 items_[ValidateItemIndex(index)].icon = icon; 248 MenuItemsChanged(); 249 } 250 251 void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) { 252 items_[ValidateItemIndex(index)].sublabel = sublabel; 253 MenuItemsChanged(); 254 } 255 256 void SimpleMenuModel::SetMinorText(int index, 257 const base::string16& minor_text) { 258 items_[ValidateItemIndex(index)].minor_text = minor_text; 259 } 260 261 void SimpleMenuModel::Clear() { 262 items_.clear(); 263 MenuItemsChanged(); 264 } 265 266 int 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 277 bool 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 286 int SimpleMenuModel::GetItemCount() const { 287 return static_cast<int>(items_.size()); 288 } 289 290 MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const { 291 return items_[ValidateItemIndex(index)].type; 292 } 293 294 ui::MenuSeparatorType SimpleMenuModel::GetSeparatorTypeAt(int index) const { 295 return items_[ValidateItemIndex(index)].separator_type; 296 } 297 298 int SimpleMenuModel::GetCommandIdAt(int index) const { 299 return items_[ValidateItemIndex(index)].command_id; 300 } 301 302 base::string16 SimpleMenuModel::GetLabelAt(int index) const { 303 if (IsItemDynamicAt(index)) 304 return delegate_->GetLabelForCommandId(GetCommandIdAt(index)); 305 return items_[ValidateItemIndex(index)].label; 306 } 307 308 base::string16 SimpleMenuModel::GetSublabelAt(int index) const { 309 if (IsItemDynamicAt(index)) 310 return delegate_->GetSublabelForCommandId(GetCommandIdAt(index)); 311 return items_[ValidateItemIndex(index)].sublabel; 312 } 313 314 base::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 320 bool SimpleMenuModel::IsItemDynamicAt(int index) const { 321 if (delegate_) 322 return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index)); 323 return false; 324 } 325 326 bool 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 335 bool 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 343 int SimpleMenuModel::GetGroupIdAt(int index) const { 344 return items_[ValidateItemIndex(index)].group_id; 345 } 346 347 bool 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 359 ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const { 360 return items_[ValidateItemIndex(index)].button_model; 361 } 362 363 bool 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 370 bool 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 377 void SimpleMenuModel::HighlightChangedTo(int index) { 378 if (delegate_) 379 delegate_->CommandIdHighlighted(GetCommandIdAt(index)); 380 } 381 382 void SimpleMenuModel::ActivatedAt(int index) { 383 if (delegate_) 384 delegate_->ExecuteCommand(GetCommandIdAt(index), 0); 385 } 386 387 void SimpleMenuModel::ActivatedAt(int index, int event_flags) { 388 if (delegate_) 389 delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags); 390 } 391 392 MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const { 393 return items_[ValidateItemIndex(index)].submenu; 394 } 395 396 void SimpleMenuModel::MenuWillShow() { 397 if (delegate_) 398 delegate_->MenuWillShow(this); 399 } 400 401 void 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 410 void SimpleMenuModel::SetMenuModelDelegate( 411 ui::MenuModelDelegate* menu_model_delegate) { 412 menu_model_delegate_ = menu_model_delegate; 413 } 414 415 MenuModelDelegate* SimpleMenuModel::GetMenuModelDelegate() const { 416 return menu_model_delegate_; 417 } 418 419 void SimpleMenuModel::OnMenuClosed() { 420 if (delegate_) 421 delegate_->MenuClosed(this); 422 } 423 424 //////////////////////////////////////////////////////////////////////////////// 425 // SimpleMenuModel, Protected: 426 427 void SimpleMenuModel::MenuItemsChanged() { 428 } 429 430 //////////////////////////////////////////////////////////////////////////////// 431 // SimpleMenuModel, Private: 432 433 int 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 439 void SimpleMenuModel::AppendItem(const Item& item) { 440 ValidateItem(item); 441 items_.push_back(item); 442 MenuItemsChanged(); 443 } 444 445 void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) { 446 ValidateItem(item); 447 items_.insert(items_.begin() + index, item); 448 MenuItemsChanged(); 449 } 450 451 void 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