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
IsCommandIdVisible(int command_id) const32 bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const {
33 return true;
34 }
35
IsItemForCommandIdDynamic(int command_id) const36 bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic(
37 int command_id) const {
38 return false;
39 }
40
GetLabelForCommandId(int command_id) const41 base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId(
42 int command_id) const {
43 return base::string16();
44 }
45
GetSublabelForCommandId(int command_id) const46 base::string16 SimpleMenuModel::Delegate::GetSublabelForCommandId(
47 int command_id) const {
48 return base::string16();
49 }
50
GetMinorTextForCommandId(int command_id) const51 base::string16 SimpleMenuModel::Delegate::GetMinorTextForCommandId(
52 int command_id) const {
53 return base::string16();
54 }
55
GetIconForCommandId(int command_id,gfx::Image * image_skia) const56 bool SimpleMenuModel::Delegate::GetIconForCommandId(
57 int command_id, gfx::Image* image_skia) const {
58 return false;
59 }
60
CommandIdHighlighted(int command_id)61 void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) {
62 }
63
ExecuteCommand(int command_id,int event_flags)64 void SimpleMenuModel::Delegate::ExecuteCommand(
65 int command_id, int event_flags) {
66 ExecuteCommand(command_id, event_flags);
67 }
68
MenuWillShow(SimpleMenuModel *)69 void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) {
70 }
71
MenuClosed(SimpleMenuModel *)72 void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) {
73 }
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // SimpleMenuModel, public:
77
SimpleMenuModel(Delegate * delegate)78 SimpleMenuModel::SimpleMenuModel(Delegate* delegate)
79 : delegate_(delegate),
80 menu_model_delegate_(NULL),
81 method_factory_(this) {
82 }
83
~SimpleMenuModel()84 SimpleMenuModel::~SimpleMenuModel() {
85 }
86
AddItem(int command_id,const base::string16 & label)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
AddItemWithStringId(int command_id,int string_id)93 void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) {
94 AddItem(command_id, l10n_util::GetStringUTF16(string_id));
95 }
96
AddCheckItem(int command_id,const base::string16 & label)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
AddCheckItemWithStringId(int command_id,int string_id)104 void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) {
105 AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id));
106 }
107
AddRadioItem(int command_id,const base::string16 & label,int group_id)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
AddRadioItemWithStringId(int command_id,int string_id,int group_id)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
AddSeparator(MenuSeparatorType separator_type)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
RemoveTrailingSeparators()143 void SimpleMenuModel::RemoveTrailingSeparators() {
144 while (!items_.empty() && items_.back().type == TYPE_SEPARATOR)
145 items_.pop_back();
146 MenuItemsChanged();
147 }
148
AddButtonItem(int command_id,ButtonMenuItemModel * model)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
AddSubMenu(int command_id,const base::string16 & label,MenuModel * model)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
AddSubMenuWithStringId(int command_id,int string_id,MenuModel * model)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
InsertItemAt(int index,int command_id,const base::string16 & label)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
InsertItemWithStringIdAt(int index,int command_id,int string_id)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
InsertSeparatorAt(int index,MenuSeparatorType separator_type)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
InsertCheckItemAt(int index,int command_id,const base::string16 & label)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
InsertCheckItemWithStringIdAt(int index,int command_id,int string_id)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
InsertRadioItemAt(int index,int command_id,const base::string16 & label,int group_id)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
InsertRadioItemWithStringIdAt(int index,int command_id,int string_id,int group_id)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
InsertSubMenuAt(int index,int command_id,const base::string16 & label,MenuModel * model)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
InsertSubMenuWithStringIdAt(int index,int command_id,int string_id,MenuModel * model)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
RemoveItemAt(int index)241 void SimpleMenuModel::RemoveItemAt(int index) {
242 items_.erase(items_.begin() + ValidateItemIndex(index));
243 MenuItemsChanged();
244 }
245
SetIcon(int index,const gfx::Image & icon)246 void SimpleMenuModel::SetIcon(int index, const gfx::Image& icon) {
247 items_[ValidateItemIndex(index)].icon = icon;
248 MenuItemsChanged();
249 }
250
SetSublabel(int index,const base::string16 & sublabel)251 void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) {
252 items_[ValidateItemIndex(index)].sublabel = sublabel;
253 MenuItemsChanged();
254 }
255
SetMinorText(int index,const base::string16 & minor_text)256 void SimpleMenuModel::SetMinorText(int index,
257 const base::string16& minor_text) {
258 items_[ValidateItemIndex(index)].minor_text = minor_text;
259 }
260
Clear()261 void SimpleMenuModel::Clear() {
262 items_.clear();
263 MenuItemsChanged();
264 }
265
GetIndexOfCommandId(int command_id)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
HasIcons() const277 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
GetItemCount() const286 int SimpleMenuModel::GetItemCount() const {
287 return static_cast<int>(items_.size());
288 }
289
GetTypeAt(int index) const290 MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const {
291 return items_[ValidateItemIndex(index)].type;
292 }
293
GetSeparatorTypeAt(int index) const294 ui::MenuSeparatorType SimpleMenuModel::GetSeparatorTypeAt(int index) const {
295 return items_[ValidateItemIndex(index)].separator_type;
296 }
297
GetCommandIdAt(int index) const298 int SimpleMenuModel::GetCommandIdAt(int index) const {
299 return items_[ValidateItemIndex(index)].command_id;
300 }
301
GetLabelAt(int index) const302 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
GetSublabelAt(int index) const308 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
GetMinorTextAt(int index) const314 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
IsItemDynamicAt(int index) const320 bool SimpleMenuModel::IsItemDynamicAt(int index) const {
321 if (delegate_)
322 return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index));
323 return false;
324 }
325
GetAcceleratorAt(int index,ui::Accelerator * accelerator) const326 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
IsItemCheckedAt(int index) const335 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
GetGroupIdAt(int index) const343 int SimpleMenuModel::GetGroupIdAt(int index) const {
344 return items_[ValidateItemIndex(index)].group_id;
345 }
346
GetIconAt(int index,gfx::Image * icon)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
GetButtonMenuItemAt(int index) const359 ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const {
360 return items_[ValidateItemIndex(index)].button_model;
361 }
362
IsEnabledAt(int index) const363 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
IsVisibleAt(int index) const370 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
HighlightChangedTo(int index)377 void SimpleMenuModel::HighlightChangedTo(int index) {
378 if (delegate_)
379 delegate_->CommandIdHighlighted(GetCommandIdAt(index));
380 }
381
ActivatedAt(int index)382 void SimpleMenuModel::ActivatedAt(int index) {
383 if (delegate_)
384 delegate_->ExecuteCommand(GetCommandIdAt(index), 0);
385 }
386
ActivatedAt(int index,int event_flags)387 void SimpleMenuModel::ActivatedAt(int index, int event_flags) {
388 if (delegate_)
389 delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags);
390 }
391
GetSubmenuModelAt(int index) const392 MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const {
393 return items_[ValidateItemIndex(index)].submenu;
394 }
395
MenuWillShow()396 void SimpleMenuModel::MenuWillShow() {
397 if (delegate_)
398 delegate_->MenuWillShow(this);
399 }
400
MenuClosed()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
SetMenuModelDelegate(ui::MenuModelDelegate * menu_model_delegate)410 void SimpleMenuModel::SetMenuModelDelegate(
411 ui::MenuModelDelegate* menu_model_delegate) {
412 menu_model_delegate_ = menu_model_delegate;
413 }
414
GetMenuModelDelegate() const415 MenuModelDelegate* SimpleMenuModel::GetMenuModelDelegate() const {
416 return menu_model_delegate_;
417 }
418
OnMenuClosed()419 void SimpleMenuModel::OnMenuClosed() {
420 if (delegate_)
421 delegate_->MenuClosed(this);
422 }
423
424 ////////////////////////////////////////////////////////////////////////////////
425 // SimpleMenuModel, Protected:
426
MenuItemsChanged()427 void SimpleMenuModel::MenuItemsChanged() {
428 }
429
430 ////////////////////////////////////////////////////////////////////////////////
431 // SimpleMenuModel, Private:
432
ValidateItemIndex(int index) const433 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
AppendItem(const Item & item)439 void SimpleMenuModel::AppendItem(const Item& item) {
440 ValidateItem(item);
441 items_.push_back(item);
442 MenuItemsChanged();
443 }
444
InsertItemAtIndex(const Item & item,int index)445 void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) {
446 ValidateItem(item);
447 items_.insert(items_.begin() + index, item);
448 MenuItemsChanged();
449 }
450
ValidateItem(const Item & item)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