15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/native_menu_win.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <Windowsx.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h" 135e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/wrapped_window_proc.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/accelerators/accelerator.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util_win.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/models/menu_model.h" 19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/keycodes/keyboard_codes.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h" 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/font_list.h" 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/geometry/rect.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h" 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/text_utils.h" 26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/win/hwnd_util.h" 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme.h" 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/native_theme/native_theme_win.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_2.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_config.h" 31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/controls/menu/menu_insertion_delegate_win.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_listener.h" 3368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/views/layout/layout_constants.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ui::NativeTheme; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The width of an icon, including the pixels between the icon and 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the item label. 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kIconWidth = 23; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Margins between the top of the item and the label. 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kItemTopMargin = 3; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Margins between the bottom of the item and the label. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kItemBottomMargin = 4; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Margins between the left of the item and the icon. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kItemLeftMargin = 4; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The width for displaying the sub-menu arrow. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kArrowWidth = 10; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct NativeMenuWin::ItemData { 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The Windows API requires that whoever creates the menus must own the 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // strings used for labels, and keep them around for the lifetime of the 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // created menu. So be it. 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 label; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Someone needs to own submenus, it may as well be us. 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<Menu2> submenu; 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We need a pointer back to the containing menu in various circumstances. 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* native_menu_win; 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The index of the item within the menu's model. 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int model_index; 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the NativeMenuWin for a particular HMENU. 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static NativeMenuWin* GetNativeMenuWinFromHMENU(HMENU hmenu) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUINFO mi = {0}; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.cbSize = sizeof(mi); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.fMask = MIM_MENUDATA | MIM_STYLE; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetMenuInfo(hmenu, &mi); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return reinterpret_cast<NativeMenuWin*>(mi.dwMenuData); 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A window that receives messages from Windows relevant to the native menu 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// structure we have constructed in NativeMenuWin. 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NativeMenuWin::MenuHostWindow { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) explicit MenuHostWindow(NativeMenuWin* parent) : parent_(parent) { 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) RegisterClass(); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName, 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); 84d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) gfx::CheckWindowCreated(hwnd_); 85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) gfx::SetWindowUserData(hwnd_, this); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ~MenuHostWindow() { 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DestroyWindow(hwnd_); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HWND hwnd() const { return hwnd_; } 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const wchar_t* kWindowClassName; 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void RegisterClass() { 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool registered = false; 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (registered) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WNDCLASSEX window_class; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::InitializeWindowClass( 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kWindowClassName, 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &base::win::WrappedWindowProc<MenuHostWindowProc>, 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CS_DBLCLKS, 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0, 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 0, 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &window_class); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ATOM clazz = RegisterClassEx(&window_class); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CHECK(clazz); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) registered = true; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Converts the WPARAM value passed to WM_MENUSELECT into an index 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // corresponding to the menu item that was selected. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int GetMenuItemIndexFromWPARAM(HMENU menu, WPARAM w_param) const { 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int count = GetMenuItemCount(menu); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // For normal command menu items, Windows passes a command id as the LOWORD 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // of WPARAM for WM_MENUSELECT. We need to walk forward through the menu 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // items to find an item with a matching ID. Ugh! 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = 0; i < count; ++i) { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask = MIIM_ID; 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetMenuItemInfo(menu, i, MF_BYPOSITION, &mii); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (mii.wID == w_param) 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return i; 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we didn't find a matching command ID, this means a submenu has been 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // selected instead, and rather than passing a command ID in 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // LOWORD(w_param), Windows has actually passed us a position, so we just 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // return it. 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return w_param; 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin::ItemData* GetItemData(ULONG_PTR item_data) { 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return reinterpret_cast<NativeMenuWin::ItemData*>(item_data); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Called when the user selects a specific item. 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void OnMenuCommand(int position, HMENU menu) { 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* menu_win = GetNativeMenuWinFromHMENU(menu); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::MenuModel* model = menu_win->model_; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* root_menu = menu_win; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (root_menu->parent_) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) root_menu = root_menu->parent_; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Only notify the model if it didn't already send out notification. 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // See comment in MenuMessageHook for details. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (root_menu->menu_action_ == MenuWrapper::MENU_ACTION_NONE) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model->ActivatedAt(position); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Called as the user moves their mouse or arrows through the contents of the 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // menu. 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void OnMenuSelect(WPARAM w_param, HMENU menu) { 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!menu) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; // menu is null when closing on XP. 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int position = GetMenuItemIndexFromWPARAM(menu, w_param); 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (position >= 0) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetNativeMenuWinFromHMENU(menu)->model_->HighlightChangedTo(position); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Called by Windows to measure the size of an owner-drawn menu item. 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* measure_item_struct) { 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin::ItemData* data = GetItemData(measure_item_struct->itemData); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data) { 1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::FontList font_list; 1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) measure_item_struct->itemWidth = 1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::GetStringWidth(data->label, font_list) + 17868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) kIconWidth + kItemLeftMargin + views::kItemLabelSpacing - 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetSystemMetrics(SM_CXMENUCHECK); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data->submenu.get()) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) measure_item_struct->itemWidth += kArrowWidth; 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the label contains an accelerator, make room for tab. 1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (data->label.find(L'\t') != base::string16::npos) 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) measure_item_struct->itemWidth += gfx::GetStringWidth(L" ", font_list); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) measure_item_struct->itemHeight = 1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) font_list.GetHeight() + kItemBottomMargin + kItemTopMargin; 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Measure separator size. 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) measure_item_struct->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) measure_item_struct->itemWidth = 0; 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Called by Windows to paint an owner-drawn menu item. 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void OnDrawItem(UINT w_param, DRAWITEMSTRUCT* draw_item_struct) { 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HDC dc = draw_item_struct->hDC; 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) COLORREF prev_bg_color, prev_text_color; 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set background color and text color 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (draw_item_struct->itemState & ODS_SELECTED) { 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_text_color = SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_MENU)); 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (draw_item_struct->itemState & ODS_DISABLED) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_text_color = SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_text_color = SetTextColor(dc, GetSysColor(COLOR_MENUTEXT)); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (draw_item_struct->itemData) { 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin::ItemData* data = GetItemData(draw_item_struct->itemData); 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw the background. 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HBRUSH hbr = CreateSolidBrush(GetBkColor(dc)); 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FillRect(dc, &draw_item_struct->rcItem, hbr); 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DeleteObject(hbr); 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw the label. 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) RECT rect = draw_item_struct->rcItem; 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rect.top += kItemTopMargin; 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Should we add kIconWidth only when icon.width() != 0 ? 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rect.left += kItemLeftMargin + kIconWidth; 22368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) rect.right -= views::kItemLabelSpacing; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT format = DT_TOP | DT_SINGLELINE; 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check whether the mnemonics should be underlined. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL underline_mnemonics; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!underline_mnemonics) 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format |= DT_HIDEPREFIX; 2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::FontList font_list; 2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HGDIOBJ old_font = static_cast<HFONT>( 2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SelectObject(dc, font_list.GetPrimaryFont().GetNativeFont())); 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If an accelerator is specified (with a tab delimiting the rest of the 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // label from the accelerator), we have to justify the fist part on the 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // left and the accelerator on the right. 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // window system UI font and will not hit here. 2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 label = data->label; 2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 accel; 2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16::size_type tab_pos = label.find(L'\t'); 2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (tab_pos != base::string16::npos) { 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) accel = label.substr(tab_pos); 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) label = label.substr(0, tab_pos); 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DrawTextEx(dc, const_cast<wchar_t*>(label.data()), 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL); 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!accel.empty()) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DrawTextEx(dc, const_cast<wchar_t*>(accel.data()), 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int>(accel.size()), &rect, 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format | DT_RIGHT, NULL); 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SelectObject(dc, old_font); 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::MenuModel::ItemType type = 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data->native_menu_win->model_->GetTypeAt(data->model_index); 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw the icon after the label, otherwise it would be covered 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // by the label. 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::Image icon; 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data->native_menu_win->model_->GetIconAt(data->model_index, &icon)) { 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We currently don't support items with both icons and checkboxes. 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const gfx::ImageSkia* skia_icon = icon.ToImageSkia(); 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(type != ui::MenuModel::TYPE_CHECK); 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::Canvas canvas( 26568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) skia_icon->GetRepresentation(1.0f), 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) false); 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) skia::DrawToNativeContext( 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.sk_canvas(), dc, 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.left + kItemLeftMargin, 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.top + (draw_item_struct->rcItem.bottom - 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.top - skia_icon->height()) / 2, NULL); 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (type == ui::MenuModel::TYPE_CHECK && 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data->native_menu_win->model_->IsItemCheckedAt( 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data->model_index)) { 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Manually render a checkbox. 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::NativeThemeWin* native_theme = ui::NativeThemeWin::instance(); 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const MenuConfig& config = MenuConfig::instance(native_theme); 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeTheme::State state; 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (draw_item_struct->itemState & ODS_DISABLED) { 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state = NativeTheme::kDisabled; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state = draw_item_struct->itemState & ODS_SELECTED ? 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeTheme::kHovered : NativeTheme::kNormal; 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int height = 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top; 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int icon_y = kItemTopMargin + 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (height - kItemTopMargin - kItemBottomMargin - 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) config.check_height) / 2; 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::Canvas canvas(gfx::Size(config.check_width, config.check_height), 29168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 1.0f, 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) false); 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeTheme::ExtraParams extra; 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra.menu_check.is_radio = false; 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gfx::Rect bounds(0, 0, config.check_width, config.check_height); 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw the background and the check. 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) native_theme->Paint( 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.sk_canvas(), NativeTheme::kMenuCheckBackground, 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state, bounds, extra); 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) native_theme->Paint( 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) canvas.sk_canvas(), NativeTheme::kMenuCheck, state, bounds, extra); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw checkbox to menu. 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) skia::DrawToNativeContext(canvas.sk_canvas(), dc, 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.left + kItemLeftMargin, 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.top + (draw_item_struct->rcItem.bottom - 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.top - config.check_height) / 2, NULL); 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Draw the separator 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) draw_item_struct->rcItem.top += 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top) / 3; 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DrawEdge(dc, &draw_item_struct->rcItem, EDGE_ETCHED, BF_TOP); 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetBkColor(dc, prev_bg_color); 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetTextColor(dc, prev_text_color); 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool ProcessWindowMessage(HWND window, 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT message, 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WPARAM w_param, 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LPARAM l_param, 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LRESULT* l_result) { 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (message) { 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case WM_MENUCOMMAND: 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param)); 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *l_result = 0; 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case WM_MENUSELECT: 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OnMenuSelect(LOWORD(w_param), reinterpret_cast<HMENU>(l_param)); 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *l_result = 0; 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case WM_MEASUREITEM: 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OnMeasureItem(w_param, reinterpret_cast<MEASUREITEMSTRUCT*>(l_param)); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *l_result = 0; 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case WM_DRAWITEM: 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OnDrawItem(w_param, reinterpret_cast<DRAWITEMSTRUCT*>(l_param)); 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *l_result = 0; 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(beng): bring over owner draw from old menu system. 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static LRESULT CALLBACK MenuHostWindowProc(HWND window, 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT message, 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WPARAM w_param, 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LPARAM l_param) { 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MenuHostWindow* host = 354d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) reinterpret_cast<MenuHostWindow*>(gfx::GetWindowUserData(window)); 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // host is null during initial construction. 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LRESULT l_result = 0; 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!host || !host->ProcessWindowMessage(window, message, w_param, l_param, 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &l_result)) { 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return DefWindowProc(window, message, w_param, l_param); 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l_result; 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HWND hwnd_; 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* parent_; 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(MenuHostWindow); 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct NativeMenuWin::HighlightedMenuItemInfo { 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HighlightedMenuItemInfo() 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : has_parent(false), 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) has_submenu(false), 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu(NULL), 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) position(-1) { 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_parent; 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_submenu; 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The menu and position. These are only set for non-disabled menu items. 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* menu; 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int position; 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t* NativeMenuWin::MenuHostWindow::kWindowClassName = 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) L"ViewsMenuHostWindow"; 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NativeMenuWin, public: 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NativeMenuWin::NativeMenuWin(ui::MenuModel* model, HWND system_menu_for) 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : model_(model), 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_(NULL), 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) owner_draw_(l10n_util::NeedOverrideDefaultUIFont(NULL, NULL) && 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !system_menu_for), 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) system_menu_for_(system_menu_for), 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) first_item_index_(0), 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_action_(MENU_ACTION_NONE), 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_(NULL), 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) position_to_select_(-1), 403c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) menu_to_select_factory_(this), 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_(NULL), 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) destroyed_flag_(NULL) { 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NativeMenuWin::~NativeMenuWin() { 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (destroyed_flag_) 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *destroyed_flag_ = true; 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) STLDeleteContainerPointers(items_.begin(), items_.end()); 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DestroyMenu(menu_); 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NativeMenuWin, MenuWrapper implementation: 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) { 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CreateHostWindow(); 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdateStates(); 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RECURSE; 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flags |= GetAlignmentFlags(alignment); 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_action_ = MENU_ACTION_NONE; 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set a hook function so we can listen for keyboard events while the 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // menu is open, and store a pointer to this object in a static 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // variable so the hook has access to it (ugly, but it's the 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // only way). 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) open_native_menu_win_ = this; 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HHOOK hhook = SetWindowsHookEx(WH_MSGFILTER, MenuMessageHook, 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetModuleHandle(NULL), ::GetCurrentThreadId()); 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Mark that any registered listeners have not been called for this particular 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // opening of the menu. 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) listeners_called_ = false; 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Command dispatch is done through WM_MENUCOMMAND, handled by the host 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // window. 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HWND hwnd = host_window_->hwnd(); 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_ = NULL; 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) position_to_select_ = -1; 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_factory_.InvalidateWeakPtrs(); 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool destroyed = false; 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) destroyed_flag_ = &destroyed; 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->MenuWillShow(); 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TrackPopupMenu(menu_, flags, point.x(), point.y(), 0, host_window_->hwnd(), 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL); 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UnhookWindowsHookEx(hhook); 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) open_native_menu_win_ = NULL; 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (destroyed) 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) destroyed_flag_ = NULL; 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (menu_to_select_) { 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Folks aren't too happy if we notify immediately. In particular, notifying 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the delegate can cause destruction leaving the stack in a weird 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // state. Instead post a task, then notify. This mirrors what WM_MENUCOMMAND 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // does. 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_factory_.InvalidateWeakPtrs(); 459c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::MessageLoop::current()->PostTask( 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FROM_HERE, 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&NativeMenuWin::DelayedSelect, 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_factory_.GetWeakPtr())); 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_action_ = MENU_ACTION_SELECTED; 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Send MenuClosed after we schedule the select, otherwise MenuClosed is 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // processed after the select (MenuClosed posts a delayed task too). 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->MenuClosed(); 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::CancelMenu() { 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EndMenu(); 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 474c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NativeMenuWin::Rebuild(MenuInsertionDelegateWin* delegate) { 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResetNativeMenu(); 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items_.clear(); 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) owner_draw_ = model_->HasIcons() || owner_draw_; 4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : 0; 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int menu_index = first_item_index_; 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) { 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int model_index = menu_index - first_item_index_; 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SEPARATOR) 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddSeparatorItemAt(menu_index, model_index); 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddMenuItemAt(menu_index, model_index); 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::UpdateStates() { 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A depth-first walk of the menu items, updating states. 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int model_index = 0; 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<ItemData*>::const_iterator it; 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (it = items_.begin(); it != items_.end(); ++it, ++model_index) { 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int menu_index = model_index + first_item_index_; 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetMenuItemState(menu_index, model_->IsEnabledAt(model_index), 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->IsItemCheckedAt(model_index), false); 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (model_->IsItemDynamicAt(model_index)) { 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(atwilson): Update the icon as well (http://crbug.com/66508). 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetMenuItemLabel(menu_index, model_index, 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->GetLabelAt(model_index)); 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Menu2* submenu = (*it)->submenu.get(); 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (submenu) 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) submenu->UpdateStates(); 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HMENU NativeMenuWin::GetNativeMenu() const { 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return menu_; 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NativeMenuWin::MenuAction NativeMenuWin::GetMenuAction() const { 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return menu_action_; 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::AddMenuListener(MenuListener* listener) { 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) listeners_.AddObserver(listener); 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::RemoveMenuListener(MenuListener* listener) { 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) listeners_.RemoveObserver(listener); 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::SetMinimumWidth(int width) { 5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIMPLEMENTED(); 5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NativeMenuWin, private: 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NativeMenuWin* NativeMenuWin::open_native_menu_win_ = NULL; 5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::DelayedSelect() { 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (menu_to_select_) 5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_to_select_->model_->ActivatedAt(position_to_select_); 5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NativeMenuWin::GetHighlightedMenuItemInfo( 5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMENU menu, 5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HighlightedMenuItemInfo* info) { 5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = 0; i < ::GetMenuItemCount(menu); i++) { 5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT state = ::GetMenuState(menu, i, MF_BYPOSITION); 5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state & MF_HILITE) { 5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (state & MF_POPUP) { 5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMENU submenu = GetSubMenu(menu, i); 5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (GetHighlightedMenuItemInfo(submenu, info)) 5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->has_parent = true; 5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->has_submenu = true; 5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (!(state & MF_SEPARATOR) && !(state & MF_DISABLED)) { 5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->menu = GetNativeMenuWinFromHMENU(menu); 5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->position = i; 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT CALLBACK NativeMenuWin::MenuMessageHook( 5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int n_code, WPARAM w_param, LPARAM l_param) { 5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LRESULT result = CallNextHookEx(NULL, n_code, w_param, l_param); 5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NativeMenuWin* this_ptr = open_native_menu_win_; 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this_ptr) 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The first time this hook is called, that means the menu has successfully 5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // opened, so call the callback function on all of our listeners. 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this_ptr->listeners_called_) { 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FOR_EACH_OBSERVER(MenuListener, this_ptr->listeners_, OnMenuOpened()); 5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this_ptr->listeners_called_ = true; 5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MSG* msg = reinterpret_cast<MSG*>(l_param); 5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (msg->message == WM_LBUTTONUP || msg->message == WM_RBUTTONUP) { 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HighlightedMenuItemInfo info; 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (GetHighlightedMenuItemInfo(this_ptr->menu_, &info) && info.menu) { 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It appears that when running a menu by way of TrackPopupMenu(Ex) win32 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // gets confused if the underlying window paints itself. As its very easy 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // for the underlying window to repaint itself (especially since some menu 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // items trigger painting of the tabstrip on mouse over) we have this 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // workaround. When the mouse is released on a menu item we remember the 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // menu item and end the menu. When the nested message loop returns we 5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // schedule a task to notify the model. It's still possible to get a 5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // WM_MENUCOMMAND, so we have to be careful that we don't notify the model 5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // twice. 5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this_ptr->menu_to_select_ = info.menu; 5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this_ptr->position_to_select_ = info.position; 5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EndMenu(); 5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (msg->message == WM_KEYDOWN) { 5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HighlightedMenuItemInfo info; 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (GetHighlightedMenuItemInfo(this_ptr->menu_, &info)) { 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (msg->wParam == VK_LEFT && !info.has_parent) { 6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this_ptr->menu_action_ = MENU_ACTION_PREVIOUS; 6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::EndMenu(); 6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (msg->wParam == VK_RIGHT && !info.has_parent && 6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !info.has_submenu) { 6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this_ptr->menu_action_ = MENU_ACTION_NEXT; 6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ::EndMenu(); 6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const { 6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask = MIIM_FTYPE; 6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii); 6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return !!(mii.fType & MF_SEPARATOR); 6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) { 6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA; 6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!owner_draw_) 6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fType = MFT_STRING; 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fType = MFT_OWNERDRAW; 6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ItemData* item_data = new ItemData; 6315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) item_data->label = base::string16(); 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); 6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (type == ui::MenuModel::TYPE_SUBMENU) { 6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index))); 6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask |= MIIM_SUBMENU; 6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.hSubMenu = item_data->submenu->GetNativeMenu(); 6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetNativeMenuWinFromHMENU(mii.hSubMenu)->parent_ = this; 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (type == ui::MenuModel::TYPE_RADIO) 6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fType |= MFT_RADIOCHECK; 6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.wID = model_->GetCommandIdAt(model_index); 6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_data->native_menu_win = this; 6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) item_data->model_index = model_index; 6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items_.insert(items_.begin() + model_index, item_data); 6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data); 6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdateMenuItemInfoForString(&mii, model_index, 6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->GetLabelAt(model_index)); 6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InsertMenuItem(menu_, menu_index, TRUE, &mii); 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) { 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask = MIIM_FTYPE; 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fType = MFT_SEPARATOR; 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Insert a dummy entry into our label list so we can index directly into it 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // using item indices if need be. 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items_.insert(items_.begin() + model_index, new ItemData); 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InsertMenuItem(menu_, menu_index, TRUE, &mii); 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::SetMenuItemState(int menu_index, bool enabled, bool checked, 6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_default) { 6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsSeparatorItemAt(menu_index)) 6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT state = enabled ? MFS_ENABLED : MFS_DISABLED; 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (checked) 6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state |= MFS_CHECKED; 6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_default) 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state |= MFS_DEFAULT; 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fMask = MIIM_STATE; 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.fState = state; 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii); 6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::SetMenuItemLabel(int menu_index, 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int model_index, 6835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::string16& label) { 6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsSeparatorItemAt(menu_index)) 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUITEMINFO mii = {0}; 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii.cbSize = sizeof(mii); 6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdateMenuItemInfoForString(&mii, model_index, label); 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii); 6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::UpdateMenuItemInfoForString(MENUITEMINFO* mii, 6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int model_index, 6955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::string16& label) { 6965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 formatted = label; 6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::MenuModel::ItemType type = model_->GetTypeAt(model_index); 6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Strip out any tabs, otherwise they get interpreted as accelerators and can 6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // lead to weird behavior. 7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReplaceSubstringsAfterOffset(&formatted, 0, L"\t", L" "); 7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (type != ui::MenuModel::TYPE_SUBMENU) { 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Add accelerator details to the label if provided. 7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::Accelerator accelerator(ui::VKEY_UNKNOWN, ui::EF_NONE); 7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (model_->GetAcceleratorAt(model_index, &accelerator)) { 7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) formatted += L"\t"; 7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) formatted += accelerator.GetShortcutText(); 7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update the owned string, since Windows will want us to keep this new 7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // version around. 7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items_[model_index]->label = formatted; 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Give Windows a pointer to the label string. 7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii->fMask |= MIIM_STRING; 7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mii->dwTypeData = 7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const_cast<wchar_t*>(items_[model_index]->label.c_str()); 7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)UINT NativeMenuWin::GetAlignmentFlags(int alignment) const { 7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UINT alignment_flags = TPM_TOPALIGN; 7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (alignment == Menu2::ALIGN_TOPLEFT) 7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) alignment_flags |= TPM_LEFTALIGN; 7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else if (alignment == Menu2::ALIGN_TOPRIGHT) 7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) alignment_flags |= TPM_RIGHTALIGN; 7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return alignment_flags; 7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::ResetNativeMenu() { 7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsWindow(system_menu_for_)) { 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (menu_) 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetSystemMenu(system_menu_for_, TRUE); 7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_ = GetSystemMenu(system_menu_for_, FALSE); 7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (menu_) 7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DestroyMenu(menu_); 7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_ = CreatePopupMenu(); 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Rather than relying on the return value of TrackPopupMenuEx, which is 7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // always a command identifier, instead we tell the menu to notify us via 7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // our host window and the WM_MENUCOMMAND message. 7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MENUINFO mi = {0}; 7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.cbSize = sizeof(mi); 7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.fMask = MIM_STYLE | MIM_MENUDATA; 7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.dwStyle = MNS_NOTIFYBYPOS; 7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mi.dwMenuData = reinterpret_cast<ULONG_PTR>(this); 7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetMenuInfo(menu_, &mi); 7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NativeMenuWin::CreateHostWindow() { 7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This only gets called from RunMenuAt, and as such there is only ever one 7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // host window per menu hierarchy, no matter how many NativeMenuWin objects 7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // exist wrapping submenus. 7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!host_window_.get()) 7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) host_window_.reset(new MenuHostWindow(this)); 7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MenuWrapper, public: 7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MenuWrapper* MenuWrapper::CreateWrapper(ui::MenuModel* model) { 7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return new NativeMenuWin(model, NULL); 7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace views 767