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