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