Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(809)

Unified Diff: views/controls/menu/native_menu_win.cc

Issue 119237: A new menu system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « views/controls/menu/native_menu_win.h ('k') | views/controls/menu/simple_menu_model.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: views/controls/menu/native_menu_win.cc
===================================================================
--- views/controls/menu/native_menu_win.cc (revision 0)
+++ views/controls/menu/native_menu_win.cc (revision 0)
@@ -0,0 +1,346 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
+// source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+#include "views/controls/menu/native_menu_win.h"
+
+#include "app/l10n_util.h"
+#include "app/l10n_util_win.h"
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+#include "views/accelerator.h"
+#include "views/controls/menu/menu_2.h"
+
+namespace views {
+
+struct NativeMenuWin::ItemData {
+ // The Windows API requires that whoever creates the menus must own the
+ // strings used for labels, and keep them around for the lifetime of the
+ // created menu. So be it.
+ std::wstring label;
+
+ // Someone needs to own submenus, it may as well be us.
+ scoped_ptr<Menu2> submenu;
+};
+
+// TODO(beng): bring over owner draw from old menu system.
+class NativeMenuWin::MenuHostWindow {
+ public:
+ MenuHostWindow() {
+ RegisterClass();
+ hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName,
+ L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+ SetProp(hwnd_, kMenuHostWindowKey, this);
+ }
+
+ ~MenuHostWindow() {
+ DestroyWindow(hwnd_);
+ }
+
+ HWND hwnd() const { return hwnd_; }
+
+ private:
+ static const wchar_t* kMenuHostWindowKey;
+ static const wchar_t* kWindowClassName;
+
+ void RegisterClass() {
+ static bool registered = false;
+ if (registered)
+ return;
+
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = &MenuHostWindowProc;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszClassName = kWindowClassName;
+ ATOM clazz = RegisterClassEx(&wcex);
+ DCHECK(clazz);
+ registered = true;
+ }
+
+ bool ProcessWindowMessage(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* l_result) {
+ return false;
+ }
+
+ static LRESULT CALLBACK MenuHostWindowProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ MenuHostWindow* host =
+ reinterpret_cast<MenuHostWindow*>(GetProp(window, kMenuHostWindowKey));
+ LRESULT l_result = 0;
+ if (!host || !host->ProcessWindowMessage(window, message, w_param, l_param,
+ &l_result)) {
+ return DefWindowProc(window, message, w_param, l_param);
+ }
+ return l_result;
+ }
+
+ HWND hwnd_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuHostWindow);
+};
+
+// static
+const wchar_t* NativeMenuWin::MenuHostWindow::kWindowClassName =
+ L"ViewsMenuHostWindow";
+
+const wchar_t* NativeMenuWin::MenuHostWindow::kMenuHostWindowKey =
+ L"__MENU_HOST_WINDOW__";
+
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuWin, public:
+
+NativeMenuWin::NativeMenuWin(Menu2Model* model,
+ Menu2Delegate* delegate,
+ HWND system_menu_for)
+ : model_(model),
+ delegate_(delegate),
+ menu_(NULL),
+ owner_draw_(false),
+ system_menu_for_(system_menu_for),
+ first_item_index_(0) {
+}
+
+NativeMenuWin::~NativeMenuWin() {
+ STLDeleteContainerPointers(items_.begin(), items_.end());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuWin, MenuWrapper implementation:
+
+void NativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) {
+ CreateHostWindow();
+ UpdateStates();
+ UINT flags = TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE;
+ flags |= GetAlignmentFlags(alignment);
+ UINT selected_command_id = TrackPopupMenuEx(menu_, flags, point.x(),
+ point.y(), host_window_->hwnd(),
+ NULL);
+ if (selected_command_id > 0) {
+ // Locate the correct delegate and model to notify about the selection.
+ // See comment in GetMenuForCommandId for details.
+ NativeMenuWin* menu = GetMenuForCommandId(selected_command_id);
+ menu->delegate_->ExecuteCommand(menu->model_, selected_command_id);
+ }
+}
+
+void NativeMenuWin::Rebuild() {
+ ResetNativeMenu();
+ owner_draw_ = model_->HasIcons();
+ first_item_index_ = model_->GetFirstItemIndex(GetNativeMenu());
+ for (int menu_index = first_item_index_;
+ menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) {
+ int model_index = menu_index - first_item_index_;
+ if (model_->GetTypeAt(model_index) == Menu2Model::TYPE_SEPARATOR)
+ AddSeparatorItemAt(menu_index, model_index);
+ else
+ AddMenuItemAt(menu_index, model_index);
+ }
+}
+
+void NativeMenuWin::UpdateStates() {
+ // A depth-first walk of the menu items, updating states.
+ for (int menu_index = first_item_index_;
+ menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) {
+ int model_index = menu_index - first_item_index_;
+ SetMenuItemState(menu_index, model_->IsEnabledAt(model_index),
+ model_->IsItemCheckedAt(model_index), false);
+ if (model_->IsLabelDynamicAt(model_index)) {
+ SetMenuItemLabel(menu_index, model_index,
+ model_->GetLabelAt(model_index));
+ }
+ Menu2* submenu = items_.at(model_index)->submenu.get();
+ if (submenu)
+ submenu->UpdateStates();
+ }
+}
+
+gfx::NativeMenu NativeMenuWin::GetNativeMenu() const {
+ return menu_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuWin, private:
+
+bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_FTYPE;
+ GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+ return !!(mii.fType & MF_SEPARATOR);
+}
+
+void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA;
+ if (!owner_draw_)
+ mii.fType = MFT_STRING;
+ else
+ mii.fType = MFT_OWNERDRAW;
+ mii.dwItemData = reinterpret_cast<ULONG_PTR>(this);
+
+ ItemData* item_data = new ItemData;
+ Menu2Model::ItemType type = model_->GetTypeAt(model_index);
+ if (type == Menu2Model::TYPE_SUBMENU) {
+ item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index),
+ delegate_));
+ mii.fMask |= MIIM_SUBMENU;
+ mii.hSubMenu = item_data->submenu->GetNativeMenu();
+ } else {
+ if (type == Menu2Model::TYPE_RADIO)
+ mii.fType |= MFT_RADIOCHECK;
+ mii.wID = model_->GetCommandIdAt(model_index);
+ }
+ items_.insert(items_.begin() + model_index, item_data);
+ UpdateMenuItemInfoForString(&mii, model_index,
+ model_->GetLabelAt(model_index));
+ InsertMenuItem(menu_, menu_index, TRUE, &mii);
+}
+
+void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) {
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_FTYPE;
+ mii.fType = MFT_SEPARATOR;
+ // Insert a dummy entry into our label list so we can index directly into it
+ // using item indices if need be.
+ items_.insert(items_.begin() + model_index, new ItemData);
+ InsertMenuItem(menu_, menu_index, TRUE, &mii);
+}
+
+void NativeMenuWin::SetMenuItemState(int menu_index, bool enabled, bool checked,
+ bool is_default) {
+ if (IsSeparatorItemAt(menu_index))
+ return;
+
+ UINT state = enabled ? MFS_ENABLED : MFS_DISABLED;
+ if (checked)
+ state |= MFS_CHECKED;
+ if (is_default)
+ state |= MFS_DEFAULT;
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = state;
+ SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+}
+
+void NativeMenuWin::SetMenuItemLabel(int menu_index,
+ int model_index,
+ const std::wstring& label) {
+ if (IsSeparatorItemAt(menu_index))
+ return;
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ UpdateMenuItemInfoForString(&mii, model_index, label);
+ if (!owner_draw_)
+ SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+}
+
+void NativeMenuWin::UpdateMenuItemInfoForString(
+ MENUITEMINFO* mii,
+ int model_index,
+ const std::wstring& label) {
+ std::wstring formatted = label;
+ Menu2Model::ItemType type = model_->GetTypeAt(model_index);
+ if (type != Menu2Model::TYPE_SUBMENU) {
+ // Add accelerator details to the label if provided.
+ views::Accelerator accelerator(0, false, false, false);
+ if (model_->GetAcceleratorAt(model_index, &accelerator)) {
+ formatted += L"\t";
+ formatted += accelerator.GetShortcutText();
+ }
+ }
+
+ // Update the owned string, since Windows will want us to keep this new
+ // version around.
+ items_[model_index]->label = formatted;
+
+ // Windows only requires a pointer to the label string if it's going to be
+ // doing the drawing.
+ if (!owner_draw_) {
+ mii->fMask |= MIIM_STRING;
+ mii->dwTypeData =
+ const_cast<wchar_t*>(items_.at(model_index)->label.c_str());
+ }
+}
+
+NativeMenuWin* NativeMenuWin::GetMenuForCommandId(UINT command_id) const {
+ // Menus can have nested submenus. In the views Menu system, each submenu is
+ // wrapped in a NativeMenu instance, which may have a different model and
+ // delegate from the parent menu. The trouble is, RunMenuAt is called on the
+ // parent NativeMenuWin, and so it's not possible to assume that we can just
+ // dispatch the command id returned by TrackPopupMenuEx to the parent's
+ // delegate. For this reason, we stow a pointer on every menu item we create
+ // to the NativeMenuWin that most closely contains it. Fortunately, Windows
+ // provides GetMenuItemInfo, which can walk down the menu item tree from
+ // the root |menu_| to find the data for a given item even if it's in a
+ // submenu.
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA;
+ GetMenuItemInfo(menu_, command_id, FALSE, &mii);
+ return reinterpret_cast<NativeMenuWin*>(mii.dwItemData);
+}
+
+UINT NativeMenuWin::GetAlignmentFlags(int alignment) const {
+ bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
+ UINT alignment_flags = TPM_TOPALIGN;
+ if (alignment == Menu2::ALIGN_TOPLEFT)
+ alignment_flags |= TPM_LEFTALIGN;
+ else if (alignment == Menu2::ALIGN_TOPRIGHT)
+ alignment_flags |= TPM_RIGHTALIGN;
+ return alignment_flags;
+}
+
+void NativeMenuWin::ResetNativeMenu() {
+ if (IsWindow(system_menu_for_)) {
+ if (menu_)
+ GetSystemMenu(system_menu_for_, TRUE);
+ menu_ = GetSystemMenu(system_menu_for_, FALSE);
+ } else {
+ if (menu_)
+ DestroyMenu(menu_);
+ menu_ = CreatePopupMenu();
+ }
+}
+
+void NativeMenuWin::CreateHostWindow() {
+ if (!host_window_.get())
+ host_window_.reset(new MenuHostWindow());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SystemMenuModel:
+
+SystemMenuModel::SystemMenuModel(SimpleMenuModel::Delegate* delegate)
+ : SimpleMenuModel(delegate) {
+}
+
+SystemMenuModel::~SystemMenuModel() {
+}
+
+int SystemMenuModel::GetFirstItemIndex(gfx::NativeMenu native_menu) const {
+ // We allow insertions before last item (Close).
+ return std::max(0, GetMenuItemCount(native_menu) - 1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MenuWrapper, public:
+
+// static
+MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
+ return new NativeMenuWin(menu->model(), menu->delegate(), NULL);
+}
+
+} // namespace views
« no previous file with comments | « views/controls/menu/native_menu_win.h ('k') | views/controls/menu/simple_menu_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698