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/views/controls/menu/menu_model_adapter.h"
6
7#include "base/logging.h"
8#include "ui/base/l10n/l10n_util.h"
9#include "ui/base/models/menu_model.h"
10#include "ui/gfx/image/image.h"
11#include "ui/views/controls/menu/submenu_view.h"
12#include "ui/views/views_delegate.h"
13
14namespace views {
15
16MenuModelAdapter::MenuModelAdapter(ui::MenuModel* menu_model)
17    : menu_model_(menu_model),
18      triggerable_event_flags_(ui::EF_LEFT_MOUSE_BUTTON |
19                               ui::EF_RIGHT_MOUSE_BUTTON) {
20  DCHECK(menu_model);
21}
22
23MenuModelAdapter::~MenuModelAdapter() {
24}
25
26void MenuModelAdapter::BuildMenu(MenuItemView* menu) {
27  DCHECK(menu);
28
29  // Clear the menu.
30  if (menu->HasSubmenu()) {
31    const int subitem_count = menu->GetSubmenu()->child_count();
32    for (int i = 0; i < subitem_count; ++i)
33      menu->RemoveMenuItemAt(0);
34  }
35
36  // Leave entries in the map if the menu is being shown.  This
37  // allows the map to find the menu model of submenus being closed
38  // so ui::MenuModel::MenuClosed() can be called.
39  if (!menu->GetMenuController())
40    menu_map_.clear();
41  menu_map_[menu] = menu_model_;
42
43  // Repopulate the menu.
44  BuildMenuImpl(menu, menu_model_);
45  menu->ChildrenChanged();
46}
47
48MenuItemView* MenuModelAdapter::CreateMenu() {
49  MenuItemView* item = new MenuItemView(this);
50  BuildMenu(item);
51  return item;
52}
53
54// Static.
55MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model,
56                                                       int model_index,
57                                                       MenuItemView* menu,
58                                                       int menu_index,
59                                                       int item_id) {
60  gfx::Image icon;
61  model->GetIconAt(model_index, &icon);
62  string16 label, sublabel, minor_text;
63  ui::MenuSeparatorType separator_style = ui::NORMAL_SEPARATOR;
64  MenuItemView::Type type;
65  ui::MenuModel::ItemType menu_type = model->GetTypeAt(model_index);
66
67  switch (menu_type) {
68    case ui::MenuModel::TYPE_COMMAND:
69      type = MenuItemView::NORMAL;
70      label = model->GetLabelAt(model_index);
71      sublabel = model->GetSublabelAt(model_index);
72      minor_text = model->GetMinorTextAt(model_index);
73      break;
74    case ui::MenuModel::TYPE_CHECK:
75      type = MenuItemView::CHECKBOX;
76      label = model->GetLabelAt(model_index);
77      sublabel = model->GetSublabelAt(model_index);
78      minor_text = model->GetMinorTextAt(model_index);
79      break;
80    case ui::MenuModel::TYPE_RADIO:
81      type = MenuItemView::RADIO;
82      label = model->GetLabelAt(model_index);
83      sublabel = model->GetSublabelAt(model_index);
84      minor_text = model->GetMinorTextAt(model_index);
85      break;
86    case ui::MenuModel::TYPE_SEPARATOR:
87      icon = gfx::Image();
88      type = MenuItemView::SEPARATOR;
89      separator_style = model->GetSeparatorTypeAt(model_index);
90      break;
91    case ui::MenuModel::TYPE_SUBMENU:
92      type = MenuItemView::SUBMENU;
93      label = model->GetLabelAt(model_index);
94      sublabel = model->GetSublabelAt(model_index);
95      minor_text = model->GetMinorTextAt(model_index);
96      break;
97    default:
98      NOTREACHED();
99      type = MenuItemView::NORMAL;
100      break;
101  }
102
103  return menu->AddMenuItemAt(
104      menu_index,
105      item_id,
106      label,
107      sublabel,
108      minor_text,
109      icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(),
110      type,
111      separator_style);
112}
113
114// Static.
115MenuItemView* MenuModelAdapter::AppendMenuItemFromModel(ui::MenuModel* model,
116                                                        int model_index,
117                                                        MenuItemView* menu,
118                                                        int item_id) {
119  const int menu_index = menu->HasSubmenu() ?
120      menu->GetSubmenu()->child_count() : 0;
121  return AddMenuItemFromModelAt(model, model_index, menu, menu_index, item_id);
122}
123
124
125MenuItemView* MenuModelAdapter::AppendMenuItem(MenuItemView* menu,
126                                               ui::MenuModel* model,
127                                               int index) {
128  return AppendMenuItemFromModel(model, index, menu,
129                                 model->GetCommandIdAt(index));
130}
131
132// MenuModelAdapter, MenuDelegate implementation:
133
134void MenuModelAdapter::ExecuteCommand(int id) {
135  ui::MenuModel* model = menu_model_;
136  int index = 0;
137  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
138    model->ActivatedAt(index);
139    return;
140  }
141
142  NOTREACHED();
143}
144
145void MenuModelAdapter::ExecuteCommand(int id, int mouse_event_flags) {
146  ui::MenuModel* model = menu_model_;
147  int index = 0;
148  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
149    model->ActivatedAt(index, mouse_event_flags);
150    return;
151  }
152
153  NOTREACHED();
154}
155
156bool MenuModelAdapter::IsTriggerableEvent(MenuItemView* source,
157                                          const ui::Event& e) {
158  return e.type() == ui::ET_GESTURE_TAP ||
159         e.type() == ui::ET_GESTURE_TAP_DOWN ||
160         (e.IsMouseEvent() && (triggerable_event_flags_ & e.flags()) != 0);
161}
162
163bool MenuModelAdapter::GetAccelerator(int id,
164                                      ui::Accelerator* accelerator) {
165  ui::MenuModel* model = menu_model_;
166  int index = 0;
167  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
168    return model->GetAcceleratorAt(index, accelerator);
169
170  NOTREACHED();
171  return false;
172}
173
174string16 MenuModelAdapter::GetLabel(int id) const {
175  ui::MenuModel* model = menu_model_;
176  int index = 0;
177  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
178    return model->GetLabelAt(index);
179
180  NOTREACHED();
181  return string16();
182}
183
184const gfx::Font* MenuModelAdapter::GetLabelFont(int id) const {
185  ui::MenuModel* model = menu_model_;
186  int index = 0;
187  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
188    const gfx::Font* font = model->GetLabelFontAt(index);
189    if (font)
190      return font;
191  }
192
193  // This line may be reached for the empty menu item.
194  return MenuDelegate::GetLabelFont(id);
195}
196
197bool MenuModelAdapter::IsCommandEnabled(int id) const {
198  ui::MenuModel* model = menu_model_;
199  int index = 0;
200  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
201    return model->IsEnabledAt(index);
202
203  NOTREACHED();
204  return false;
205}
206
207bool MenuModelAdapter::IsItemChecked(int id) const {
208  ui::MenuModel* model = menu_model_;
209  int index = 0;
210  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
211    return model->IsItemCheckedAt(index);
212
213  NOTREACHED();
214  return false;
215}
216
217void MenuModelAdapter::SelectionChanged(MenuItemView* menu) {
218  // Ignore selection of the root menu.
219  if (menu == menu->GetRootMenuItem())
220    return;
221
222  const int id = menu->GetCommand();
223  ui::MenuModel* model = menu_model_;
224  int index = 0;
225  if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
226    model->HighlightChangedTo(index);
227    return;
228  }
229
230  NOTREACHED();
231}
232
233void MenuModelAdapter::WillShowMenu(MenuItemView* menu) {
234  // Look up the menu model for this menu.
235  const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
236      menu_map_.find(menu);
237  if (map_iterator != menu_map_.end()) {
238    map_iterator->second->MenuWillShow();
239    return;
240  }
241
242  NOTREACHED();
243}
244
245void MenuModelAdapter::WillHideMenu(MenuItemView* menu) {
246  // Look up the menu model for this menu.
247  const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
248      menu_map_.find(menu);
249  if (map_iterator != menu_map_.end()) {
250    map_iterator->second->MenuClosed();
251    return;
252  }
253
254  NOTREACHED();
255}
256
257// MenuModelAdapter, private:
258
259void MenuModelAdapter::BuildMenuImpl(MenuItemView* menu, ui::MenuModel* model) {
260  DCHECK(menu);
261  DCHECK(model);
262  bool has_icons = model->HasIcons();
263  const int item_count = model->GetItemCount();
264  for (int i = 0; i < item_count; ++i) {
265    MenuItemView* item = AppendMenuItem(menu, model, i);
266
267    if (item)
268      item->SetVisible(model->IsVisibleAt(i));
269
270    if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) {
271      DCHECK(item);
272      DCHECK_EQ(MenuItemView::SUBMENU, item->GetType());
273      ui::MenuModel* submodel = model->GetSubmenuModelAt(i);
274      DCHECK(submodel);
275      BuildMenuImpl(item, submodel);
276      has_icons = has_icons || item->has_icons();
277
278      menu_map_[item] = submodel;
279    }
280  }
281
282  menu->set_has_icons(has_icons);
283}
284
285}  // namespace views
286