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   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