Home | History | Annotate | Download | only in models
      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