| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/menu/native_menu_win.h" | 5 #include "ui/views/controls/menu/native_menu_win.h" |
| 6 | 6 |
| 7 #include <Windowsx.h> | 7 #include <Windowsx.h> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
| 12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
| 13 #include "base/string_util.h" | 13 #include "base/string_util.h" |
| 14 #include "base/win/wrapped_window_proc.h" | 14 #include "base/win/wrapped_window_proc.h" |
| 15 #include "ui/base/accelerators/accelerator.h" | 15 #include "ui/base/accelerators/accelerator.h" |
| 16 #include "ui/base/keycodes/keyboard_codes.h" | 16 #include "ui/base/keycodes/keyboard_codes.h" |
| 17 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
| 18 #include "ui/base/l10n/l10n_util_win.h" | 18 #include "ui/base/l10n/l10n_util_win.h" |
| 19 #include "ui/base/models/menu_model.h" | 19 #include "ui/base/models/menu_model.h" |
| 20 #include "ui/base/win/hwnd_util.h" | 20 #include "ui/base/win/hwnd_util.h" |
| 21 #include "ui/gfx/canvas.h" | 21 #include "ui/gfx/canvas.h" |
| 22 #include "ui/gfx/font.h" | 22 #include "ui/gfx/font.h" |
| 23 #include "ui/gfx/image/image.h" | 23 #include "ui/gfx/image/image.h" |
| 24 #include "ui/gfx/image/image_skia.h" | 24 #include "ui/gfx/image/image_skia.h" |
| 25 #include "ui/gfx/rect.h" | 25 #include "ui/gfx/rect.h" |
| 26 #include "ui/native_theme/native_theme.h" | 26 #include "ui/native_theme/native_theme.h" |
| 27 #include "ui/native_theme/native_theme_win.h" | 27 #include "ui/native_theme/native_theme_win.h" |
| 28 #include "ui/views/controls/menu/menu_2.h" | |
| 29 #include "ui/views/controls/menu/menu_config.h" | 28 #include "ui/views/controls/menu/menu_config.h" |
| 29 #include "ui/views/controls/menu/menu_insertion_delegate.h" |
| 30 #include "ui/views/controls/menu/menu_listener.h" | 30 #include "ui/views/controls/menu/menu_listener.h" |
| 31 | 31 |
| 32 using ui::NativeTheme; | 32 using ui::NativeTheme; |
| 33 | 33 |
| 34 namespace views { | 34 namespace views { |
| 35 | 35 |
| 36 // The width of an icon, including the pixels between the icon and | 36 // The width of an icon, including the pixels between the icon and |
| 37 // the item label. | 37 // the item label. |
| 38 static const int kIconWidth = 23; | 38 static const int kIconWidth = 23; |
| 39 // Margins between the top of the item and the label. | 39 // Margins between the top of the item and the label. |
| 40 static const int kItemTopMargin = 3; | 40 static const int kItemTopMargin = 3; |
| 41 // Margins between the bottom of the item and the label. | 41 // Margins between the bottom of the item and the label. |
| 42 static const int kItemBottomMargin = 4; | 42 static const int kItemBottomMargin = 4; |
| 43 // Margins between the left of the item and the icon. | 43 // Margins between the left of the item and the icon. |
| 44 static const int kItemLeftMargin = 4; | 44 static const int kItemLeftMargin = 4; |
| 45 // Margins between the right of the item and the label. | 45 // Margins between the right of the item and the label. |
| 46 static const int kItemRightMargin = 10; | 46 static const int kItemRightMargin = 10; |
| 47 // The width for displaying the sub-menu arrow. | 47 // The width for displaying the sub-menu arrow. |
| 48 static const int kArrowWidth = 10; | 48 static const int kArrowWidth = 10; |
| 49 | 49 |
| 50 struct NativeMenuWin::ItemData { | 50 struct NativeMenuWin::ItemData { |
| 51 // The Windows API requires that whoever creates the menus must own the | 51 // The Windows API requires that whoever creates the menus must own the |
| 52 // strings used for labels, and keep them around for the lifetime of the | 52 // strings used for labels, and keep them around for the lifetime of the |
| 53 // created menu. So be it. | 53 // created menu. So be it. |
| 54 string16 label; | 54 string16 label; |
| 55 | 55 |
| 56 // Someone needs to own submenus, it may as well be us. | 56 // Someone needs to own submenus, it may as well be us. |
| 57 scoped_ptr<Menu2> submenu; | 57 scoped_ptr<NativeMenuWin> submenu; |
| 58 | 58 |
| 59 // We need a pointer back to the containing menu in various circumstances. | 59 // We need a pointer back to the containing menu in various circumstances. |
| 60 NativeMenuWin* native_menu_win; | 60 NativeMenuWin* native_menu_win; |
| 61 | 61 |
| 62 // The index of the item within the menu's model. | 62 // The index of the item within the menu's model. |
| 63 int model_index; | 63 int model_index; |
| 64 }; | 64 }; |
| 65 | 65 |
| 66 // Returns the NativeMenuWin for a particular HMENU. | 66 // Returns the NativeMenuWin for a particular HMENU. |
| 67 static NativeMenuWin* GetNativeMenuWinFromHMENU(HMENU hmenu) { | 67 static NativeMenuWin* GetNativeMenuWinFromHMENU(HMENU hmenu) { |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 145 // Called when the user selects a specific item. | 145 // Called when the user selects a specific item. |
| 146 void OnMenuCommand(int position, HMENU menu) { | 146 void OnMenuCommand(int position, HMENU menu) { |
| 147 NativeMenuWin* menu_win = GetNativeMenuWinFromHMENU(menu); | 147 NativeMenuWin* menu_win = GetNativeMenuWinFromHMENU(menu); |
| 148 ui::MenuModel* model = menu_win->model_; | 148 ui::MenuModel* model = menu_win->model_; |
| 149 NativeMenuWin* root_menu = menu_win; | 149 NativeMenuWin* root_menu = menu_win; |
| 150 while (root_menu->parent_) | 150 while (root_menu->parent_) |
| 151 root_menu = root_menu->parent_; | 151 root_menu = root_menu->parent_; |
| 152 | 152 |
| 153 // Only notify the model if it didn't already send out notification. | 153 // Only notify the model if it didn't already send out notification. |
| 154 // See comment in MenuMessageHook for details. | 154 // See comment in MenuMessageHook for details. |
| 155 if (root_menu->menu_action_ == MenuWrapper::MENU_ACTION_NONE) | 155 if (root_menu->menu_action_ == MENU_ACTION_NONE) |
| 156 model->ActivatedAt(position); | 156 model->ActivatedAt(position); |
| 157 } | 157 } |
| 158 | 158 |
| 159 // Called as the user moves their mouse or arrows through the contents of the | 159 // Called as the user moves their mouse or arrows through the contents of the |
| 160 // menu. | 160 // menu. |
| 161 void OnMenuSelect(WPARAM w_param, HMENU menu) { | 161 void OnMenuSelect(WPARAM w_param, HMENU menu) { |
| 162 if (!menu) | 162 if (!menu) |
| 163 return; // menu is null when closing on XP. | 163 return; // menu is null when closing on XP. |
| 164 | 164 |
| 165 int position = GetMenuItemIndexFromWPARAM(menu, w_param); | 165 int position = GetMenuItemIndexFromWPARAM(menu, w_param); |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 destroyed_flag_(NULL) { | 403 destroyed_flag_(NULL) { |
| 404 } | 404 } |
| 405 | 405 |
| 406 NativeMenuWin::~NativeMenuWin() { | 406 NativeMenuWin::~NativeMenuWin() { |
| 407 if (destroyed_flag_) | 407 if (destroyed_flag_) |
| 408 *destroyed_flag_ = true; | 408 *destroyed_flag_ = true; |
| 409 STLDeleteContainerPointers(items_.begin(), items_.end()); | 409 STLDeleteContainerPointers(items_.begin(), items_.end()); |
| 410 DestroyMenu(menu_); | 410 DestroyMenu(menu_); |
| 411 } | 411 } |
| 412 | 412 |
| 413 //////////////////////////////////////////////////////////////////////////////// | |
| 414 // NativeMenuWin, MenuWrapper implementation: | |
| 415 | |
| 416 void NativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) { | 413 void NativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) { |
| 417 CreateHostWindow(); | 414 CreateHostWindow(); |
| 418 UpdateStates(); | 415 UpdateStates(); |
| 419 UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RECURSE; | 416 UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RECURSE; |
| 420 flags |= GetAlignmentFlags(alignment); | 417 flags |= GetAlignmentFlags(alignment); |
| 421 menu_action_ = MENU_ACTION_NONE; | 418 menu_action_ = MENU_ACTION_NONE; |
| 422 | 419 |
| 423 // Set a hook function so we can listen for keyboard events while the | 420 // Set a hook function so we can listen for keyboard events while the |
| 424 // menu is open, and store a pointer to this object in a static | 421 // menu is open, and store a pointer to this object in a static |
| 425 // variable so the hook has access to it (ugly, but it's the | 422 // variable so the hook has access to it (ugly, but it's the |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 462 } | 459 } |
| 463 // Send MenuClosed after we schedule the select, otherwise MenuClosed is | 460 // Send MenuClosed after we schedule the select, otherwise MenuClosed is |
| 464 // processed after the select (MenuClosed posts a delayed task too). | 461 // processed after the select (MenuClosed posts a delayed task too). |
| 465 model_->MenuClosed(); | 462 model_->MenuClosed(); |
| 466 } | 463 } |
| 467 | 464 |
| 468 void NativeMenuWin::CancelMenu() { | 465 void NativeMenuWin::CancelMenu() { |
| 469 EndMenu(); | 466 EndMenu(); |
| 470 } | 467 } |
| 471 | 468 |
| 472 void NativeMenuWin::Rebuild(InsertionDelegate* delegate) { | 469 void NativeMenuWin::Rebuild(MenuInsertionDelegate* delegate) { |
| 473 ResetNativeMenu(); | 470 ResetNativeMenu(); |
| 474 items_.clear(); | 471 items_.clear(); |
| 475 | 472 |
| 476 owner_draw_ = model_->HasIcons() || owner_draw_; | 473 owner_draw_ = model_->HasIcons() || owner_draw_; |
| 477 first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : 0; | 474 first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : 0; |
| 478 for (int menu_index = first_item_index_; | 475 for (int menu_index = first_item_index_; |
| 479 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) { | 476 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) { |
| 480 int model_index = menu_index - first_item_index_; | 477 int model_index = menu_index - first_item_index_; |
| 481 if (model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SEPARATOR) | 478 if (model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SEPARATOR) |
| 482 AddSeparatorItemAt(menu_index, model_index); | 479 AddSeparatorItemAt(menu_index, model_index); |
| 483 else | 480 else |
| 484 AddMenuItemAt(menu_index, model_index); | 481 AddMenuItemAt(menu_index, model_index); |
| 485 } | 482 } |
| 486 } | 483 } |
| 487 | 484 |
| 488 void NativeMenuWin::UpdateStates() { | 485 void NativeMenuWin::UpdateStates() { |
| 489 // A depth-first walk of the menu items, updating states. | 486 // A depth-first walk of the menu items, updating states. |
| 490 int model_index = 0; | 487 int model_index = 0; |
| 491 std::vector<ItemData*>::const_iterator it; | 488 std::vector<ItemData*>::const_iterator it; |
| 492 for (it = items_.begin(); it != items_.end(); ++it, ++model_index) { | 489 for (it = items_.begin(); it != items_.end(); ++it, ++model_index) { |
| 493 int menu_index = model_index + first_item_index_; | 490 int menu_index = model_index + first_item_index_; |
| 494 SetMenuItemState(menu_index, model_->IsEnabledAt(model_index), | 491 SetMenuItemState(menu_index, model_->IsEnabledAt(model_index), |
| 495 model_->IsItemCheckedAt(model_index), false); | 492 model_->IsItemCheckedAt(model_index), false); |
| 496 if (model_->IsItemDynamicAt(model_index)) { | 493 if (model_->IsItemDynamicAt(model_index)) { |
| 497 // TODO(atwilson): Update the icon as well (http://crbug.com/66508). | 494 // TODO(atwilson): Update the icon as well (http://crbug.com/66508). |
| 498 SetMenuItemLabel(menu_index, model_index, | 495 SetMenuItemLabel(menu_index, model_index, |
| 499 model_->GetLabelAt(model_index)); | 496 model_->GetLabelAt(model_index)); |
| 500 } | 497 } |
| 501 Menu2* submenu = (*it)->submenu.get(); | 498 NativeMenuWin* submenu = (*it)->submenu.get(); |
| 502 if (submenu) | 499 if (submenu) |
| 503 submenu->UpdateStates(); | 500 submenu->UpdateStates(); |
| 504 } | 501 } |
| 505 } | 502 } |
| 506 | 503 |
| 507 HMENU NativeMenuWin::GetNativeMenu() const { | 504 HMENU NativeMenuWin::GetNativeMenu() const { |
| 508 return menu_; | 505 return menu_; |
| 509 } | 506 } |
| 510 | 507 |
| 511 NativeMenuWin::MenuAction NativeMenuWin::GetMenuAction() const { | 508 NativeMenuWin::MenuAction NativeMenuWin::GetMenuAction() const { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 622 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA; | 619 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA; |
| 623 if (!owner_draw_) | 620 if (!owner_draw_) |
| 624 mii.fType = MFT_STRING; | 621 mii.fType = MFT_STRING; |
| 625 else | 622 else |
| 626 mii.fType = MFT_OWNERDRAW; | 623 mii.fType = MFT_OWNERDRAW; |
| 627 | 624 |
| 628 ItemData* item_data = new ItemData; | 625 ItemData* item_data = new ItemData; |
| 629 item_data->label = string16(); | 626 item_data->label = string16(); |
| 630 ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); | 627 ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); |
| 631 if (type == ui::MenuModel::TYPE_SUBMENU) { | 628 if (type == ui::MenuModel::TYPE_SUBMENU) { |
| 632 item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index))); | 629 item_data->submenu.reset( |
| 630 new NativeMenuWin(model_->GetSubmenuModelAt(model_index), NULL)); |
| 633 mii.fMask |= MIIM_SUBMENU; | 631 mii.fMask |= MIIM_SUBMENU; |
| 634 mii.hSubMenu = item_data->submenu->GetNativeMenu(); | 632 mii.hSubMenu = item_data->submenu->GetNativeMenu(); |
| 635 GetNativeMenuWinFromHMENU(mii.hSubMenu)->parent_ = this; | 633 GetNativeMenuWinFromHMENU(mii.hSubMenu)->parent_ = this; |
| 636 } else { | 634 } else { |
| 637 if (type == ui::MenuModel::TYPE_RADIO) | 635 if (type == ui::MenuModel::TYPE_RADIO) |
| 638 mii.fType |= MFT_RADIOCHECK; | 636 mii.fType |= MFT_RADIOCHECK; |
| 639 mii.wID = model_->GetCommandIdAt(model_index); | 637 mii.wID = model_->GetCommandIdAt(model_index); |
| 640 } | 638 } |
| 641 item_data->native_menu_win = this; | 639 item_data->native_menu_win = this; |
| 642 item_data->model_index = model_index; | 640 item_data->model_index = model_index; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 items_[model_index]->label = formatted; | 708 items_[model_index]->label = formatted; |
| 711 | 709 |
| 712 // Give Windows a pointer to the label string. | 710 // Give Windows a pointer to the label string. |
| 713 mii->fMask |= MIIM_STRING; | 711 mii->fMask |= MIIM_STRING; |
| 714 mii->dwTypeData = | 712 mii->dwTypeData = |
| 715 const_cast<wchar_t*>(items_[model_index]->label.c_str()); | 713 const_cast<wchar_t*>(items_[model_index]->label.c_str()); |
| 716 } | 714 } |
| 717 | 715 |
| 718 UINT NativeMenuWin::GetAlignmentFlags(int alignment) const { | 716 UINT NativeMenuWin::GetAlignmentFlags(int alignment) const { |
| 719 UINT alignment_flags = TPM_TOPALIGN; | 717 UINT alignment_flags = TPM_TOPALIGN; |
| 720 if (alignment == Menu2::ALIGN_TOPLEFT) | 718 if (alignment == ALIGN_TOPLEFT) |
| 721 alignment_flags |= TPM_LEFTALIGN; | 719 alignment_flags |= TPM_LEFTALIGN; |
| 722 else if (alignment == Menu2::ALIGN_TOPRIGHT) | 720 else if (alignment == ALIGN_TOPRIGHT) |
| 723 alignment_flags |= TPM_RIGHTALIGN; | 721 alignment_flags |= TPM_RIGHTALIGN; |
| 724 return alignment_flags; | 722 return alignment_flags; |
| 725 } | 723 } |
| 726 | 724 |
| 727 void NativeMenuWin::ResetNativeMenu() { | 725 void NativeMenuWin::ResetNativeMenu() { |
| 728 if (IsWindow(system_menu_for_)) { | 726 if (IsWindow(system_menu_for_)) { |
| 729 if (menu_) | 727 if (menu_) |
| 730 GetSystemMenu(system_menu_for_, TRUE); | 728 GetSystemMenu(system_menu_for_, TRUE); |
| 731 menu_ = GetSystemMenu(system_menu_for_, FALSE); | 729 menu_ = GetSystemMenu(system_menu_for_, FALSE); |
| 732 } else { | 730 } else { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 746 } | 744 } |
| 747 | 745 |
| 748 void NativeMenuWin::CreateHostWindow() { | 746 void NativeMenuWin::CreateHostWindow() { |
| 749 // This only gets called from RunMenuAt, and as such there is only ever one | 747 // This only gets called from RunMenuAt, and as such there is only ever one |
| 750 // host window per menu hierarchy, no matter how many NativeMenuWin objects | 748 // host window per menu hierarchy, no matter how many NativeMenuWin objects |
| 751 // exist wrapping submenus. | 749 // exist wrapping submenus. |
| 752 if (!host_window_.get()) | 750 if (!host_window_.get()) |
| 753 host_window_.reset(new MenuHostWindow(this)); | 751 host_window_.reset(new MenuHostWindow(this)); |
| 754 } | 752 } |
| 755 | 753 |
| 756 //////////////////////////////////////////////////////////////////////////////// | |
| 757 // MenuWrapper, public: | |
| 758 | |
| 759 // static | |
| 760 MenuWrapper* MenuWrapper::CreateWrapper(ui::MenuModel* model) { | |
| 761 return new NativeMenuWin(model, NULL); | |
| 762 } | |
| 763 | |
| 764 } // namespace views | 754 } // namespace views |
| OLD | NEW |