diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-14 21:06:57 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-14 21:06:57 +0000 |
commit | 6277eaded1883e224015efcc5456e3c265e8b633 (patch) | |
tree | 2c36feca37d7a6132fa14556fb146307285c007d /views | |
parent | aa1dddf057a4295ee1c09c0566eb98ecd8de8fb8 (diff) | |
download | chromium_src-6277eaded1883e224015efcc5456e3c265e8b633.zip chromium_src-6277eaded1883e224015efcc5456e3c265e8b633.tar.gz chromium_src-6277eaded1883e224015efcc5456e3c265e8b633.tar.bz2 |
Refactors menus so that I can create a MenuGtk implementation, and moves it into
the namespace views.
BUG=none
TEST=make sure I haven't broken any menus. This impacts all menus in
Chrome, except bookmark ones.
Review URL: http://codereview.chromium.org/113410
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16100 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/button/button_dropdown.cc | 10 | ||||
-rw-r--r-- | views/controls/menu/menu.cc | 547 | ||||
-rw-r--r-- | views/controls/menu/menu.h | 134 | ||||
-rw-r--r-- | views/controls/menu/menu_win.cc | 565 | ||||
-rw-r--r-- | views/controls/menu/menu_win.h | 129 | ||||
-rw-r--r-- | views/controls/scrollbar/bitmap_scroll_bar.cc | 25 | ||||
-rw-r--r-- | views/controls/text_field.cc | 4 | ||||
-rw-r--r-- | views/views.vcproj | 8 |
8 files changed, 768 insertions, 654 deletions
diff --git a/views/controls/button/button_dropdown.cc b/views/controls/button/button_dropdown.cc index 157caf0..6c81c24 100644 --- a/views/controls/button/button_dropdown.cc +++ b/views/controls/button/button_dropdown.cc @@ -137,7 +137,7 @@ void ButtonDropDown::ShowDropDownMenu(HWND window) { if (menu_position.x() < left_bound) menu_position.set_x(left_bound); - Menu menu(menu_delegate_, anchor, window); + scoped_ptr<Menu> menu(Menu::Create(menu_delegate_, anchor, window)); // ID's for AppendMenu is 1-based because RunMenu will ignore the user // selection if id=0 is selected (0 = NO-OP) so we add 1 here and subtract 1 @@ -145,16 +145,16 @@ void ButtonDropDown::ShowDropDownMenu(HWND window) { int item_count = menu_delegate_->GetItemCount(); for (int i = 0; i < item_count; i++) { if (menu_delegate_->IsItemSeparator(i + 1)) { - menu.AppendSeparator(); + menu->AppendSeparator(); } else { if (menu_delegate_->HasIcon(i + 1)) - menu.AppendMenuItemWithIcon(i + 1, L"", SkBitmap()); + menu->AppendMenuItemWithIcon(i + 1, L"", SkBitmap()); else - menu.AppendMenuItem(i+1, L"", Menu::NORMAL); + menu->AppendMenuItem(i+1, L"", Menu::NORMAL); } } - menu.RunMenuAt(menu_position.x(), menu_position.y()); + menu->RunMenuAt(menu_position.x(), menu_position.y()); // Need to explicitly clear mouse handler so that events get sent // properly after the menu finishes running. If we don't do this, then diff --git a/views/controls/menu/menu.cc b/views/controls/menu/menu.cc index 1597da5..6a81d9c 100644 --- a/views/controls/menu/menu.cc +++ b/views/controls/menu/menu.cc @@ -4,353 +4,31 @@ #include "views/controls/menu/menu.h" -#include <atlbase.h> -#include <atlapp.h> -#include <atlwin.h> -#include <atlcrack.h> -#include <atlframe.h> -#include <atlmisc.h> -#include <string> - -#include "app/gfx/chrome_canvas.h" -#include "app/gfx/chrome_font.h" #include "app/l10n_util.h" -#include "app/l10n_util_win.h" -#include "base/gfx/rect.h" -#include "base/logging.h" -#include "base/stl_util-inl.h" -#include "base/string_util.h" -#include "views/accelerator.h" - -const SkBitmap* Menu::Delegate::kEmptyIcon = 0; - -// The width of an icon, including the pixels between the icon and -// the item label. -static const int kIconWidth = 23; -// Margins between the top of the item and the label. -static const int kItemTopMargin = 3; -// Margins between the bottom of the item and the label. -static const int kItemBottomMargin = 4; -// Margins between the left of the item and the icon. -static const int kItemLeftMargin = 4; -// Margins between the right of the item and the label. -static const int kItemRightMargin = 10; -// The width for displaying the sub-menu arrow. -static const int kArrowWidth = 10; - -// Current active MenuHostWindow. If NULL, no menu is active. -static MenuHostWindow* active_host_window = NULL; - -// The data of menu items needed to display. -struct Menu::ItemData { - std::wstring label; - SkBitmap icon; - bool submenu; -}; - -namespace { - -static int ChromeGetMenuItemID(HMENU hMenu, int pos) { - // The built-in Windows ::GetMenuItemID doesn't work for submenus, - // so here's our own implementation. - MENUITEMINFO mii = {0}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_ID; - GetMenuItemInfo(hMenu, pos, TRUE, &mii); - return mii.wID; -} - -// MenuHostWindow ------------------------------------------------------------- - -// MenuHostWindow is the HWND the HMENU is parented to. MenuHostWindow is used -// to intercept right clicks on the HMENU and notify the delegate as well as -// for drawing icons. -// -class MenuHostWindow : public CWindowImpl<MenuHostWindow, CWindow, - CWinTraits<WS_CHILD>> { - public: - MenuHostWindow(Menu* menu, HWND parent_window) : menu_(menu) { - int extended_style = 0; - // If the menu needs to be created with a right-to-left UI layout, we must - // set the appropriate RTL flags (such as WS_EX_LAYOUTRTL) property for the - // underlying HWND. - if (menu_->delegate_->IsRightToLeftUILayout()) - extended_style |= l10n_util::GetExtendedStyles(); - Create(parent_window, gfx::Rect().ToRECT(), 0, 0, extended_style); - } - - ~MenuHostWindow() { - DestroyWindow(); - } - - DECLARE_FRAME_WND_CLASS(L"MenuHostWindow", NULL); - BEGIN_MSG_MAP(MenuHostWindow); - MSG_WM_RBUTTONUP(OnRButtonUp) - MSG_WM_MEASUREITEM(OnMeasureItem) - MSG_WM_DRAWITEM(OnDrawItem) - END_MSG_MAP(); - - private: - // NOTE: I really REALLY tried to use WM_MENURBUTTONUP, but I ran into - // two problems in using it: - // 1. It doesn't contain the coordinates of the mouse. - // 2. It isn't invoked for menuitems representing a submenu that have children - // menu items (not empty). - - void OnRButtonUp(UINT w_param, const CPoint& loc) { - int id; - if (menu_->delegate_ && FindMenuIDByLocation(menu_, loc, &id)) - menu_->delegate_->ShowContextMenu(menu_, id, loc.x, loc.y, true); - } - - void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* lpmis) { - Menu::ItemData* data = reinterpret_cast<Menu::ItemData*>(lpmis->itemData); - if (data != NULL) { - ChromeFont font; - lpmis->itemWidth = font.GetStringWidth(data->label) + kIconWidth + - kItemLeftMargin + kItemRightMargin - - GetSystemMetrics(SM_CXMENUCHECK); - if (data->submenu) - lpmis->itemWidth += kArrowWidth; - // If the label contains an accelerator, make room for tab. - if (data->label.find(L'\t') != std::wstring::npos) - lpmis->itemWidth += font.GetStringWidth(L" "); - lpmis->itemHeight = font.height() + kItemBottomMargin + kItemTopMargin; - } else { - // Measure separator size. - lpmis->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; - lpmis->itemWidth = 0; - } - } - - void OnDrawItem(UINT wParam, DRAWITEMSTRUCT* lpdis) { - HDC hDC = lpdis->hDC; - COLORREF prev_bg_color, prev_text_color; - - // Set background color and text color - if (lpdis->itemState & ODS_SELECTED) { - prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT)); - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - } else { - prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_MENU)); - if (lpdis->itemState & ODS_DISABLED) - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); - else - prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT)); - } - - if (lpdis->itemData) { - Menu::ItemData* data = - reinterpret_cast<Menu::ItemData*>(lpdis->itemData); - - // Draw the background. - HBRUSH hbr = CreateSolidBrush(GetBkColor(hDC)); - FillRect(hDC, &lpdis->rcItem, hbr); - DeleteObject(hbr); - - // Draw the label. - RECT rect = lpdis->rcItem; - rect.top += kItemTopMargin; - // Should we add kIconWidth only when icon.width() != 0 ? - rect.left += kItemLeftMargin + kIconWidth; - rect.right -= kItemRightMargin; - UINT format = DT_TOP | DT_SINGLELINE; - // Check whether the mnemonics should be underlined. - BOOL underline_mnemonics; - SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); - if (!underline_mnemonics) - format |= DT_HIDEPREFIX; - ChromeFont font; - HGDIOBJ old_font = static_cast<HFONT>(SelectObject(hDC, font.hfont())); - int fontsize = font.FontSize(); - - // If an accelerator is specified (with a tab delimiting the rest of the - // label from the accelerator), we have to justify the fist part on the - // left and the accelerator on the right. - // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the - // window system UI font and will not hit here. - std::wstring label = data->label; - std::wstring accel; - std::wstring::size_type tab_pos = label.find(L'\t'); - if (tab_pos != std::wstring::npos) { - accel = label.substr(tab_pos); - label = label.substr(0, tab_pos); - } - DrawTextEx(hDC, const_cast<wchar_t*>(label.data()), - static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL); - if (!accel.empty()) - DrawTextEx(hDC, const_cast<wchar_t*>(accel.data()), - static_cast<int>(accel.size()), &rect, - format | DT_RIGHT, NULL); - SelectObject(hDC, old_font); +#include "third_party/skia/include/core/SkBitmap.h" - // Draw the icon after the label, otherwise it would be covered - // by the label. - if (data->icon.width() != 0 && data->icon.height() != 0) { - ChromeCanvas canvas(data->icon.width(), data->icon.height(), false); - canvas.drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode); - canvas.DrawBitmapInt(data->icon, 0, 0); - canvas.getTopPlatformDevice().drawToHDC(hDC, lpdis->rcItem.left + - kItemLeftMargin, lpdis->rcItem.top + (lpdis->rcItem.bottom - - lpdis->rcItem.top - data->icon.height()) / 2, NULL); - } - - } else { - // Draw the separator - lpdis->rcItem.top += (lpdis->rcItem.bottom - lpdis->rcItem.top) / 3; - DrawEdge(hDC, &lpdis->rcItem, EDGE_ETCHED, BF_TOP); - } - - SetBkColor(hDC, prev_bg_color); - SetTextColor(hDC, prev_text_color); - } - - bool FindMenuIDByLocation(Menu* menu, const CPoint& loc, int* id) { - int index = MenuItemFromPoint(NULL, menu->menu_, loc); - if (index != -1) { - *id = ChromeGetMenuItemID(menu->menu_, index); - return true; - } else { - for (std::vector<Menu*>::iterator i = menu->submenus_.begin(); - i != menu->submenus_.end(); ++i) { - if (FindMenuIDByLocation(*i, loc, id)) - return true; - } - } - return false; - } - - // The menu that created us. - Menu* menu_; - - DISALLOW_EVIL_CONSTRUCTORS(MenuHostWindow); -}; - -} // namespace +namespace views { bool Menu::Delegate::IsRightToLeftUILayout() const { return l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT; } const SkBitmap& Menu::Delegate::GetEmptyIcon() const { - if (kEmptyIcon == NULL) - kEmptyIcon = new SkBitmap(); - return *kEmptyIcon; + static const SkBitmap* empty_icon = new SkBitmap(); + return *empty_icon; } -Menu::Menu(Delegate* delegate, AnchorPoint anchor, HWND owner) +Menu::Menu(Delegate* delegate, AnchorPoint anchor) : delegate_(delegate), - menu_(CreatePopupMenu()), - anchor_(anchor), - owner_(owner), - is_menu_visible_(false), - owner_draw_(l10n_util::NeedOverrideDefaultUIFont(NULL, NULL)) { - DCHECK(delegate_); + anchor_(anchor) { } Menu::Menu(Menu* parent) : delegate_(parent->delegate_), - menu_(CreatePopupMenu()), - anchor_(parent->anchor_), - owner_(parent->owner_), - is_menu_visible_(false), - owner_draw_(parent->owner_draw_) { -} - -Menu::Menu(HMENU hmenu) - : delegate_(NULL), - menu_(hmenu), - anchor_(TOPLEFT), - owner_(NULL), - is_menu_visible_(false), - owner_draw_(false) { - DCHECK(menu_); + anchor_(parent->anchor_) { } Menu::~Menu() { - STLDeleteContainerPointers(submenus_.begin(), submenus_.end()); - STLDeleteContainerPointers(item_data_.begin(), item_data_.end()); - DestroyMenu(menu_); -} - -UINT Menu::GetStateFlagsForItemID(int item_id) const { - // Use the delegate to get enabled and checked state. - UINT flags = - delegate_->IsCommandEnabled(item_id) ? MFS_ENABLED : MFS_DISABLED; - - if (delegate_->IsItemChecked(item_id)) - flags |= MFS_CHECKED; - - if (delegate_->IsItemDefault(item_id)) - flags |= MFS_DEFAULT; - - return flags; -} - -void Menu::AddMenuItemInternal(int index, - int item_id, - const std::wstring& label, - const SkBitmap& icon, - HMENU submenu, - MenuItemType type) { - DCHECK(type != SEPARATOR) << "Call AddSeparator instead!"; - - if (label.empty() && !delegate_) { - // No label and no delegate; don't add an empty menu. - // It appears under some circumstance we're getting an empty label - // (l10n_util::GetString(IDS_TASK_MANAGER) returns ""). This shouldn't - // happen, but I'm working over the crash here. - NOTREACHED(); - return; - } - - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE | MIIM_ID; - if (submenu) { - mii.fMask |= MIIM_SUBMENU; - mii.hSubMenu = submenu; - } - - // Set the type and ID. - if (!owner_draw_) { - mii.fType = MFT_STRING; - mii.fMask |= MIIM_STRING; - } else { - mii.fType = MFT_OWNERDRAW; - } - - if (type == RADIO) - mii.fType |= MFT_RADIOCHECK; - - mii.wID = item_id; - - // Set the item data. - Menu::ItemData* data = new ItemData; - item_data_.push_back(data); - data->submenu = submenu != NULL; - - std::wstring actual_label(label.empty() ? - delegate_->GetLabel(item_id) : label); - - // Find out if there is a shortcut we need to append to the label. - views::Accelerator accelerator(0, false, false, false); - if (delegate_ && delegate_->GetAcceleratorInfo(item_id, &accelerator)) { - actual_label += L'\t'; - actual_label += accelerator.GetShortcutText(); - } - labels_.push_back(actual_label); - - if (owner_draw_) { - if (icon.width() != 0 && icon.height() != 0) - data->icon = icon; - else - data->icon = delegate_->GetIcon(item_id); - } else { - mii.dwTypeData = const_cast<wchar_t*>(labels_.back().c_str()); - } - - InsertMenuItem(menu_, index, TRUE, &mii); } void Menu::AppendMenuItem(int item_id, @@ -366,7 +44,7 @@ void Menu::AddMenuItem(int index, if (type == SEPARATOR) AddSeparator(index); else - AddMenuItemInternal(index, item_id, label, SkBitmap(), NULL, type); + AddMenuItemInternal(index, item_id, label, SkBitmap(), type); } Menu* Menu::AppendSubMenu(int item_id, const std::wstring& label) { @@ -383,19 +61,6 @@ Menu* Menu::AppendSubMenuWithIcon(int item_id, return AddSubMenuWithIcon(-1, item_id, label, icon); } -Menu* Menu::AddSubMenuWithIcon(int index, - int item_id, - const std::wstring& label, - const SkBitmap& icon) { - if (!owner_draw_ && icon.width() != 0 && icon.height() != 0) - owner_draw_ = true; - - Menu* submenu = new Menu(this); - submenus_.push_back(submenu); - AddMenuItemInternal(index, item_id, label, icon, submenu->menu_, NORMAL); - return submenu; -} - void Menu::AppendMenuItemWithLabel(int item_id, const std::wstring& label) { AddMenuItemWithLabel(-1, item_id, label); } @@ -417,14 +82,6 @@ void Menu::AppendSeparator() { AddSeparator(-1); } -void Menu::AddSeparator(int index) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE; - mii.fType = MFT_SEPARATOR; - InsertMenuItem(menu_, index, TRUE, &mii); -} - void Menu::AppendMenuItemWithIcon(int item_id, const std::wstring& label, const SkBitmap& icon) { @@ -435,192 +92,10 @@ void Menu::AddMenuItemWithIcon(int index, int item_id, const std::wstring& label, const SkBitmap& icon) { - if (!owner_draw_) - owner_draw_ = true; - AddMenuItemInternal(index, item_id, label, icon, NULL, Menu::NORMAL); -} - -void Menu::EnableMenuItemByID(int item_id, bool enabled) { - UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; - EnableMenuItem(menu_, item_id, MF_BYCOMMAND | enable_flags); + AddMenuItemInternal(index, item_id, label, icon, Menu::NORMAL); } -void Menu::EnableMenuItemAt(int index, bool enabled) { - UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; - EnableMenuItem(menu_, index, MF_BYPOSITION | enable_flags); +Menu::Menu() : delegate_(NULL), anchor_(TOPLEFT) { } -void Menu::SetMenuLabel(int item_id, const std::wstring& label) { - MENUITEMINFO mii = {0}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STRING; - mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); - mii.cch = static_cast<UINT>(label.size()); - SetMenuItemInfo(menu_, item_id, false, &mii); -} - -DWORD Menu::GetTPMAlignFlags() const { - // The manner in which we handle the menu alignment depends on whether or not - // the menu is displayed within a mirrored view. If the UI is mirrored, the - // alignment needs to be fliped so that instead of aligning the menu to the - // right of the point, we align it to the left and vice versa. - DWORD align_flags = TPM_TOPALIGN; - switch (anchor_) { - case TOPLEFT: - if (delegate_->IsRightToLeftUILayout()) { - align_flags |= TPM_RIGHTALIGN; - } else { - align_flags |= TPM_LEFTALIGN; - } - break; - - case TOPRIGHT: - if (delegate_->IsRightToLeftUILayout()) { - align_flags |= TPM_LEFTALIGN; - } else { - align_flags |= TPM_RIGHTALIGN; - } - break; - - default: - NOTREACHED(); - return 0; - } - return align_flags; -} - -bool Menu::SetIcon(const SkBitmap& icon, int item_id) { - if (!owner_draw_) - owner_draw_ = true; - - const int num_items = GetMenuItemCount(menu_); - int sep_count = 0; - for (int i = 0; i < num_items; ++i) { - if (!(GetMenuState(menu_, i, MF_BYPOSITION) & MF_SEPARATOR)) { - if (ChromeGetMenuItemID(menu_, i) == item_id) { - item_data_[i - sep_count]->icon = icon; - // When the menu is running, we use SetMenuItemInfo to let Windows - // update the item information so that the icon being displayed - // could change immediately. - if (active_host_window) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE | MIIM_DATA; - mii.fType = MFT_OWNERDRAW; - mii.dwItemData = - reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); - SetMenuItemInfo(menu_, item_id, false, &mii); - } - return true; - } - } else { - ++sep_count; - } - } - - // Continue searching for the item in submenus. - for (size_t i = 0; i < submenus_.size(); ++i) { - if (submenus_[i]->SetIcon(icon, item_id)) - return true; - } - - return false; -} - -void Menu::SetMenuInfo() { - const int num_items = GetMenuItemCount(menu_); - int sep_count = 0; - for (int i = 0; i < num_items; ++i) { - MENUITEMINFO mii_info; - mii_info.cbSize = sizeof(mii_info); - // Get the menu's original type. - mii_info.fMask = MIIM_FTYPE; - GetMenuItemInfo(menu_, i, MF_BYPOSITION, &mii_info); - // Set item states. - if (!(mii_info.fType & MF_SEPARATOR)) { - const int id = ChromeGetMenuItemID(menu_, i); - - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_DATA | MIIM_STRING; - // We also need MFT_STRING for owner drawn items in order to let Windows - // handle the accelerators for us. - mii.fType = MFT_STRING; - if (owner_draw_) - mii.fType |= MFT_OWNERDRAW; - // If the menu originally has radiocheck type, we should follow it. - if (mii_info.fType & MFT_RADIOCHECK) - mii.fType |= MFT_RADIOCHECK; - mii.fState = GetStateFlagsForItemID(id); - - // Validate the label. If there is a contextual label, use it, otherwise - // default to the static label - std::wstring label; - if (!delegate_->GetContextualLabel(id, &label)) - label = labels_[i - sep_count]; - - if (owner_draw_) { - item_data_[i - sep_count]->label = label; - mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); - } - mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); - mii.cch = static_cast<UINT>(label.size()); - SetMenuItemInfo(menu_, i, true, &mii); - } else { - // Set data for owner drawn separators. Set dwItemData NULL to indicate - // a separator. - if (owner_draw_) { - MENUITEMINFO mii; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_FTYPE; - mii.fType = MFT_SEPARATOR | MFT_OWNERDRAW; - mii.dwItemData = NULL; - SetMenuItemInfo(menu_, i, true, &mii); - } - ++sep_count; - } - } - - for (size_t i = 0; i < submenus_.size(); ++i) - submenus_[i]->SetMenuInfo(); -} - -void Menu::RunMenuAt(int x, int y) { - SetMenuInfo(); - - delegate_->MenuWillShow(); - - // NOTE: we don't use TPM_RIGHTBUTTON here as it breaks selecting by way of - // press, drag, release. See bugs 718 and 8560. - UINT flags = - GetTPMAlignFlags() | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; - is_menu_visible_ = true; - DCHECK(owner_); - // In order for context menus on menus to work, the context menu needs to - // share the same window as the first menu is parented to. - bool created_host = false; - if (!active_host_window) { - created_host = true; - active_host_window = new MenuHostWindow(this, owner_); - } - UINT selected_id = - TrackPopupMenuEx(menu_, flags, x, y, active_host_window->m_hWnd, NULL); - if (created_host) { - delete active_host_window; - active_host_window = NULL; - } - is_menu_visible_ = false; - - // Execute the chosen command - if (selected_id != 0) - delegate_->ExecuteCommand(selected_id); -} - -void Menu::Cancel() { - DCHECK(is_menu_visible_); - EndMenu(); -} - -int Menu::ItemCount() { - return GetMenuItemCount(menu_); -} +} // namespace views diff --git a/views/controls/menu/menu.h b/views/controls/menu/menu.h index 0be9126..e07d7e5 100644 --- a/views/controls/menu/menu.h +++ b/views/controls/menu/menu.h @@ -5,34 +5,17 @@ #ifndef CONTROLS_MENU_VIEWS_MENU_H_ #define CONTROLS_MENU_VIEWS_MENU_H_ -#include <windows.h> - -#include <vector> - #include "base/basictypes.h" +#include "base/gfx/native_widget_types.h" #include "views/controls/menu/controller.h" class SkBitmap; -namespace { -class MenuHostWindow; -} - namespace views { + class Accelerator; -} - -/////////////////////////////////////////////////////////////////////////////// -// -// Menu class -// -// A wrapper around a Win32 HMENU handle that provides convenient APIs for -// menu construction, display and subsequent command execution. -// -/////////////////////////////////////////////////////////////////////////////// -class Menu { - friend class MenuHostWindow; +class Menu { public: ///////////////////////////////////////////////////////////////////////////// // @@ -131,14 +114,8 @@ class Menu { } protected: - // Returns an empty icon. Will initialize kEmptyIcon if it hasn't been - // initialized. + // Returns an empty icon. const SkBitmap& GetEmptyIcon() const; - - private: - // Will be initialized to an icon of 0 width and 0 height when first using. - // An empty icon means we don't need to draw it. - static const SkBitmap* kEmptyIcon; }; // This class is a helper that simply wraps a controller and forwards all @@ -195,15 +172,18 @@ class Menu { // owner The window that the menu is being brought up relative // to. Not actually used for anything but must not be // NULL. - Menu(Delegate* delegate, AnchorPoint anchor, HWND owner); - // Alternatively, a Menu object can be constructed wrapping an existing - // HMENU. This can be used to use the convenience methods to insert - // menu items and manage label string ownership. However this kind of - // Menu object cannot use the delegate. - explicit Menu(HMENU hmenu); + Menu(Delegate* delegate, AnchorPoint anchor); + Menu(); virtual ~Menu(); + static Menu* Create(Delegate* delegate, + AnchorPoint anchor, + gfx::NativeView parent); + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + Delegate* delegate() const { return delegate_; } + + AnchorPoint anchor() const { return anchor_; } // Adds an item to this menu. // item_id The id of the item, used to identify it in delegate callbacks @@ -234,10 +214,10 @@ class Menu { Menu* AppendSubMenuWithIcon(int item_id, const std::wstring& label, const SkBitmap& icon); - Menu* AddSubMenuWithIcon(int index, - int item_id, - const std::wstring& label, - const SkBitmap& icon); + virtual Menu* AddSubMenuWithIcon(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon) = 0; // This is a convenience for standard text label menu items where the label // is provided with this call. @@ -251,7 +231,7 @@ class Menu { // Adds a separator to this menu void AppendSeparator(); - void AddSeparator(int index); + virtual void AddSeparator(int index) = 0; // Appends a menu item with an icon. This is for the menu item which // needs an icon. Calling this function forces the Menu class to draw @@ -265,91 +245,47 @@ class Menu { const SkBitmap& icon); // Enables or disables the item with the specified id. - void EnableMenuItemByID(int item_id, bool enabled); - void EnableMenuItemAt(int index, bool enabled); + virtual void EnableMenuItemByID(int item_id, bool enabled) = 0; + virtual void EnableMenuItemAt(int index, bool enabled) = 0; // Sets menu label at specified index. - void SetMenuLabel(int item_id, const std::wstring& label); + virtual void SetMenuLabel(int item_id, const std::wstring& label) = 0; // Sets an icon for an item with a given item_id. Calling this function // also forces the Menu class to draw the menu, instead of relying on Windows. // Returns false if the item with |item_id| is not found. - bool SetIcon(const SkBitmap& icon, int item_id); + virtual bool SetIcon(const SkBitmap& icon, int item_id) = 0; // Shows the menu, blocks until the user dismisses the menu or selects an // item, and executes the command for the selected item (if any). // Warning: Blocking call. Will implicitly run a message loop. - void RunMenuAt(int x, int y); + virtual void RunMenuAt(int x, int y) = 0; // Cancels the menu. - virtual void Cancel(); + virtual void Cancel() = 0; // Returns the number of menu items. - int ItemCount(); + virtual int ItemCount() = 0; protected: - // The delegate that is being used to get information about the presentation. - Delegate* delegate_; - - private: - // The data of menu items needed to display. - struct ItemData; - explicit Menu(Menu* parent); - void AddMenuItemInternal(int index, - int item_id, - const std::wstring& label, - const SkBitmap& icon, - HMENU submenu, - MenuItemType type); - - // Sets menu information before displaying, including sub-menus. - void SetMenuInfo(); - - // Get all the state flags for the |fState| field of MENUITEMINFO for the - // item with the specified id. |delegate| is consulted if non-NULL about - // the state of the item in preference to |controller_|. - UINT GetStateFlagsForItemID(int item_id) const; - - // Gets the Win32 TPM alignment flags for the specified AnchorPoint. - DWORD GetTPMAlignFlags() const; - - // The Win32 Menu Handle we wrap - HMENU menu_; - - // The window that would receive WM_COMMAND messages when the user selects - // an item from the menu. - HWND owner_; + virtual void AddMenuItemInternal(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon, + MenuItemType type) = 0; - // This list is used to store the default labels for the menu items. - // We may use contextual labels when RunMenu is called, so we must save - // a copy of default ones here. - std::vector<std::wstring> labels_; - - // A flag to indicate whether this menu will be drawn by the Menu class. - // If it's true, all the menu items will be owner drawn. Otherwise, - // all the drawing will be done by Windows. - bool owner_draw_; + private: + // The delegate that is being used to get information about the presentation. + Delegate* delegate_; // How this popup menu should be aligned relative to the point it is run at. AnchorPoint anchor_; - // This list is to store the string labels and icons to display. It's used - // when owner_draw_ is true. We give MENUITEMINFO pointers to these - // structures to specify what we'd like to draw. If owner_draw_ is false, - // we only give MENUITEMINFO pointers to the labels_. - // The label member of the ItemData structure comes from either labels_ or - // the GetContextualLabel. - std::vector<ItemData*> item_data_; - - // Our sub-menus, if any. - std::vector<Menu*> submenus_; - - // Whether the menu is visible. - bool is_menu_visible_; - DISALLOW_COPY_AND_ASSIGN(Menu); }; +} // namespace views + #endif // CONTROLS_MENU_VIEWS_MENU_H_ diff --git a/views/controls/menu/menu_win.cc b/views/controls/menu/menu_win.cc new file mode 100644 index 0000000..9c85b54 --- /dev/null +++ b/views/controls/menu/menu_win.cc @@ -0,0 +1,565 @@ +// Copyright (c) 2006-2008 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/menu_win.h" + +#include <atlbase.h> +#include <atlapp.h> +#include <atlwin.h> +#include <atlcrack.h> +#include <atlframe.h> +#include <atlmisc.h> +#include <string> + +#include "app/gfx/chrome_canvas.h" +#include "app/gfx/chrome_font.h" +#include "app/l10n_util.h" +#include "app/l10n_util_win.h" +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/stl_util-inl.h" +#include "base/string_util.h" +#include "views/accelerator.h" + +namespace views { + +// The width of an icon, including the pixels between the icon and +// the item label. +static const int kIconWidth = 23; +// Margins between the top of the item and the label. +static const int kItemTopMargin = 3; +// Margins between the bottom of the item and the label. +static const int kItemBottomMargin = 4; +// Margins between the left of the item and the icon. +static const int kItemLeftMargin = 4; +// Margins between the right of the item and the label. +static const int kItemRightMargin = 10; +// The width for displaying the sub-menu arrow. +static const int kArrowWidth = 10; + +// Current active MenuHostWindow. If NULL, no menu is active. +static MenuHostWindow* active_host_window = NULL; + +// The data of menu items needed to display. +struct MenuWin::ItemData { + std::wstring label; + SkBitmap icon; + bool submenu; +}; + +namespace { + +static int ChromeGetMenuItemID(HMENU hMenu, int pos) { + // The built-in Windows ::GetMenuItemID doesn't work for submenus, + // so here's our own implementation. + MENUITEMINFO mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_ID; + GetMenuItemInfo(hMenu, pos, TRUE, &mii); + return mii.wID; +} + +// MenuHostWindow ------------------------------------------------------------- + +// MenuHostWindow is the HWND the HMENU is parented to. MenuHostWindow is used +// to intercept right clicks on the HMENU and notify the delegate as well as +// for drawing icons. +// +class MenuHostWindow : public CWindowImpl<MenuHostWindow, CWindow, + CWinTraits<WS_CHILD>> { + public: + MenuHostWindow(MenuWin* menu, HWND parent_window) : menu_(menu) { + int extended_style = 0; + // If the menu needs to be created with a right-to-left UI layout, we must + // set the appropriate RTL flags (such as WS_EX_LAYOUTRTL) property for the + // underlying HWND. + if (menu_->delegate()->IsRightToLeftUILayout()) + extended_style |= l10n_util::GetExtendedStyles(); + Create(parent_window, gfx::Rect().ToRECT(), 0, 0, extended_style); + } + + ~MenuHostWindow() { + DestroyWindow(); + } + + DECLARE_FRAME_WND_CLASS(L"MenuHostWindow", NULL); + BEGIN_MSG_MAP(MenuHostWindow); + MSG_WM_RBUTTONUP(OnRButtonUp) + MSG_WM_MEASUREITEM(OnMeasureItem) + MSG_WM_DRAWITEM(OnDrawItem) + END_MSG_MAP(); + + private: + // NOTE: I really REALLY tried to use WM_MENURBUTTONUP, but I ran into + // two problems in using it: + // 1. It doesn't contain the coordinates of the mouse. + // 2. It isn't invoked for menuitems representing a submenu that have children + // menu items (not empty). + + void OnRButtonUp(UINT w_param, const CPoint& loc) { + int id; + if (menu_->delegate() && FindMenuIDByLocation(menu_, loc, &id)) + menu_->delegate()->ShowContextMenu(menu_, id, loc.x, loc.y, true); + } + + void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* lpmis) { + MenuWin::ItemData* data = + reinterpret_cast<MenuWin::ItemData*>(lpmis->itemData); + if (data != NULL) { + ChromeFont font; + lpmis->itemWidth = font.GetStringWidth(data->label) + kIconWidth + + kItemLeftMargin + kItemRightMargin - + GetSystemMetrics(SM_CXMENUCHECK); + if (data->submenu) + lpmis->itemWidth += kArrowWidth; + // If the label contains an accelerator, make room for tab. + if (data->label.find(L'\t') != std::wstring::npos) + lpmis->itemWidth += font.GetStringWidth(L" "); + lpmis->itemHeight = font.height() + kItemBottomMargin + kItemTopMargin; + } else { + // Measure separator size. + lpmis->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; + lpmis->itemWidth = 0; + } + } + + void OnDrawItem(UINT wParam, DRAWITEMSTRUCT* lpdis) { + HDC hDC = lpdis->hDC; + COLORREF prev_bg_color, prev_text_color; + + // Set background color and text color + if (lpdis->itemState & ODS_SELECTED) { + prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT)); + prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } else { + prev_bg_color = SetBkColor(hDC, GetSysColor(COLOR_MENU)); + if (lpdis->itemState & ODS_DISABLED) + prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); + else + prev_text_color = SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT)); + } + + if (lpdis->itemData) { + MenuWin::ItemData* data = + reinterpret_cast<MenuWin::ItemData*>(lpdis->itemData); + + // Draw the background. + HBRUSH hbr = CreateSolidBrush(GetBkColor(hDC)); + FillRect(hDC, &lpdis->rcItem, hbr); + DeleteObject(hbr); + + // Draw the label. + RECT rect = lpdis->rcItem; + rect.top += kItemTopMargin; + // Should we add kIconWidth only when icon.width() != 0 ? + rect.left += kItemLeftMargin + kIconWidth; + rect.right -= kItemRightMargin; + UINT format = DT_TOP | DT_SINGLELINE; + // Check whether the mnemonics should be underlined. + BOOL underline_mnemonics; + SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); + if (!underline_mnemonics) + format |= DT_HIDEPREFIX; + ChromeFont font; + HGDIOBJ old_font = static_cast<HFONT>(SelectObject(hDC, font.hfont())); + int fontsize = font.FontSize(); + + // If an accelerator is specified (with a tab delimiting the rest of the + // label from the accelerator), we have to justify the fist part on the + // left and the accelerator on the right. + // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the + // window system UI font and will not hit here. + std::wstring label = data->label; + std::wstring accel; + std::wstring::size_type tab_pos = label.find(L'\t'); + if (tab_pos != std::wstring::npos) { + accel = label.substr(tab_pos); + label = label.substr(0, tab_pos); + } + DrawTextEx(hDC, const_cast<wchar_t*>(label.data()), + static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL); + if (!accel.empty()) + DrawTextEx(hDC, const_cast<wchar_t*>(accel.data()), + static_cast<int>(accel.size()), &rect, + format | DT_RIGHT, NULL); + SelectObject(hDC, old_font); + + // Draw the icon after the label, otherwise it would be covered + // by the label. + if (data->icon.width() != 0 && data->icon.height() != 0) { + ChromeCanvas canvas(data->icon.width(), data->icon.height(), false); + canvas.drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode); + canvas.DrawBitmapInt(data->icon, 0, 0); + canvas.getTopPlatformDevice().drawToHDC(hDC, lpdis->rcItem.left + + kItemLeftMargin, lpdis->rcItem.top + (lpdis->rcItem.bottom - + lpdis->rcItem.top - data->icon.height()) / 2, NULL); + } + + } else { + // Draw the separator + lpdis->rcItem.top += (lpdis->rcItem.bottom - lpdis->rcItem.top) / 3; + DrawEdge(hDC, &lpdis->rcItem, EDGE_ETCHED, BF_TOP); + } + + SetBkColor(hDC, prev_bg_color); + SetTextColor(hDC, prev_text_color); + } + + bool FindMenuIDByLocation(MenuWin* menu, const CPoint& loc, int* id) { + int index = MenuItemFromPoint(NULL, menu->menu_, loc); + if (index != -1) { + *id = ChromeGetMenuItemID(menu->menu_, index); + return true; + } else { + for (std::vector<MenuWin*>::iterator i = menu->submenus_.begin(); + i != menu->submenus_.end(); ++i) { + if (FindMenuIDByLocation(*i, loc, id)) + return true; + } + } + return false; + } + + // The menu that created us. + MenuWin* menu_; + + DISALLOW_COPY_AND_ASSIGN(MenuHostWindow); +}; + +} // namespace + +// static +Menu* Menu::Create(Delegate* delegate, + AnchorPoint anchor, + gfx::NativeView parent) { + return new MenuWin(delegate, anchor, parent); +} + +MenuWin::MenuWin(Delegate* d, AnchorPoint anchor, HWND owner) + : Menu(d, anchor), + menu_(CreatePopupMenu()), + owner_(owner), + is_menu_visible_(false), + owner_draw_(l10n_util::NeedOverrideDefaultUIFont(NULL, NULL)) { + DCHECK(delegate()); +} + +MenuWin::MenuWin(HMENU hmenu) + : Menu(NULL, TOPLEFT), + menu_(hmenu), + owner_(NULL), + is_menu_visible_(false), + owner_draw_(false) { + DCHECK(menu_); +} + +MenuWin::~MenuWin() { + STLDeleteContainerPointers(submenus_.begin(), submenus_.end()); + STLDeleteContainerPointers(item_data_.begin(), item_data_.end()); + DestroyMenu(menu_); +} + +Menu* MenuWin::AddSubMenuWithIcon(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon) { + MenuWin* submenu = new MenuWin(this); + submenus_.push_back(submenu); + AddMenuItemInternal(index, item_id, label, icon, submenu->menu_, NORMAL); + return submenu; +} + +void MenuWin::AddSeparator(int index) { + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + InsertMenuItem(menu_, index, TRUE, &mii); +} + +void MenuWin::EnableMenuItemByID(int item_id, bool enabled) { + UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; + EnableMenuItem(menu_, item_id, MF_BYCOMMAND | enable_flags); +} + +void MenuWin::EnableMenuItemAt(int index, bool enabled) { + UINT enable_flags = enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED; + EnableMenuItem(menu_, index, MF_BYPOSITION | enable_flags); +} + +void MenuWin::SetMenuLabel(int item_id, const std::wstring& label) { + MENUITEMINFO mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STRING; + mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); + mii.cch = static_cast<UINT>(label.size()); + SetMenuItemInfo(menu_, item_id, false, &mii); +} + +bool MenuWin::SetIcon(const SkBitmap& icon, int item_id) { + if (!owner_draw_) + owner_draw_ = true; + + const int num_items = GetMenuItemCount(menu_); + int sep_count = 0; + for (int i = 0; i < num_items; ++i) { + if (!(GetMenuState(menu_, i, MF_BYPOSITION) & MF_SEPARATOR)) { + if (ChromeGetMenuItemID(menu_, i) == item_id) { + item_data_[i - sep_count]->icon = icon; + // When the menu is running, we use SetMenuItemInfo to let Windows + // update the item information so that the icon being displayed + // could change immediately. + if (active_host_window) { + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE | MIIM_DATA; + mii.fType = MFT_OWNERDRAW; + mii.dwItemData = + reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); + SetMenuItemInfo(menu_, item_id, false, &mii); + } + return true; + } + } else { + ++sep_count; + } + } + + // Continue searching for the item in submenus. + for (size_t i = 0; i < submenus_.size(); ++i) { + if (submenus_[i]->SetIcon(icon, item_id)) + return true; + } + + return false; +} + +void MenuWin::RunMenuAt(int x, int y) { + SetMenuInfo(); + + delegate()->MenuWillShow(); + + // NOTE: we don't use TPM_RIGHTBUTTON here as it breaks selecting by way of + // press, drag, release. See bugs 718 and 8560. + UINT flags = + GetTPMAlignFlags() | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; + is_menu_visible_ = true; + DCHECK(owner_); + // In order for context menus on menus to work, the context menu needs to + // share the same window as the first menu is parented to. + bool created_host = false; + if (!active_host_window) { + created_host = true; + active_host_window = new MenuHostWindow(this, owner_); + } + UINT selected_id = + TrackPopupMenuEx(menu_, flags, x, y, active_host_window->m_hWnd, NULL); + if (created_host) { + delete active_host_window; + active_host_window = NULL; + } + is_menu_visible_ = false; + + // Execute the chosen command + if (selected_id != 0) + delegate()->ExecuteCommand(selected_id); +} + +void MenuWin::Cancel() { + DCHECK(is_menu_visible_); + EndMenu(); +} + +int MenuWin::ItemCount() { + return GetMenuItemCount(menu_); +} + +void MenuWin::AddMenuItemInternal(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon, + MenuItemType type) { + AddMenuItemInternal(index, item_id, label, icon, NULL, type); +} + +void MenuWin::AddMenuItemInternal(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon, + HMENU submenu, + MenuItemType type) { + DCHECK(type != SEPARATOR) << "Call AddSeparator instead!"; + + if (!owner_draw_ && !icon.empty()) + owner_draw_ = true; + + if (label.empty() && !delegate()) { + // No label and no delegate; don't add an empty menu. + // It appears under some circumstance we're getting an empty label + // (l10n_util::GetString(IDS_TASK_MANAGER) returns ""). This shouldn't + // happen, but I'm working over the crash here. + NOTREACHED(); + return; + } + + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE | MIIM_ID; + if (submenu) { + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = submenu; + } + + // Set the type and ID. + if (!owner_draw_) { + mii.fType = MFT_STRING; + mii.fMask |= MIIM_STRING; + } else { + mii.fType = MFT_OWNERDRAW; + } + + if (type == RADIO) + mii.fType |= MFT_RADIOCHECK; + + mii.wID = item_id; + + // Set the item data. + MenuWin::ItemData* data = new ItemData; + item_data_.push_back(data); + data->submenu = submenu != NULL; + + std::wstring actual_label(label.empty() ? + delegate()->GetLabel(item_id) : label); + + // Find out if there is a shortcut we need to append to the label. + views::Accelerator accelerator(0, false, false, false); + if (delegate() && delegate()->GetAcceleratorInfo(item_id, &accelerator)) { + actual_label += L'\t'; + actual_label += accelerator.GetShortcutText(); + } + labels_.push_back(actual_label); + + if (owner_draw_) { + if (icon.width() != 0 && icon.height() != 0) + data->icon = icon; + else + data->icon = delegate()->GetIcon(item_id); + } else { + mii.dwTypeData = const_cast<wchar_t*>(labels_.back().c_str()); + } + + InsertMenuItem(menu_, index, TRUE, &mii); +} + +MenuWin::MenuWin(MenuWin* parent) + : Menu(parent->delegate(), parent->anchor()), + menu_(CreatePopupMenu()), + owner_(parent->owner_), + is_menu_visible_(false), + owner_draw_(parent->owner_draw_) { +} + +void MenuWin::SetMenuInfo() { + const int num_items = GetMenuItemCount(menu_); + int sep_count = 0; + for (int i = 0; i < num_items; ++i) { + MENUITEMINFO mii_info; + mii_info.cbSize = sizeof(mii_info); + // Get the menu's original type. + mii_info.fMask = MIIM_FTYPE; + GetMenuItemInfo(menu_, i, MF_BYPOSITION, &mii_info); + // Set item states. + if (!(mii_info.fType & MF_SEPARATOR)) { + const int id = ChromeGetMenuItemID(menu_, i); + + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_DATA | MIIM_STRING; + // We also need MFT_STRING for owner drawn items in order to let Windows + // handle the accelerators for us. + mii.fType = MFT_STRING; + if (owner_draw_) + mii.fType |= MFT_OWNERDRAW; + // If the menu originally has radiocheck type, we should follow it. + if (mii_info.fType & MFT_RADIOCHECK) + mii.fType |= MFT_RADIOCHECK; + mii.fState = GetStateFlagsForItemID(id); + + // Validate the label. If there is a contextual label, use it, otherwise + // default to the static label + std::wstring label; + if (!delegate()->GetContextualLabel(id, &label)) + label = labels_[i - sep_count]; + + if (owner_draw_) { + item_data_[i - sep_count]->label = label; + mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data_[i - sep_count]); + } + mii.dwTypeData = const_cast<wchar_t*>(label.c_str()); + mii.cch = static_cast<UINT>(label.size()); + SetMenuItemInfo(menu_, i, true, &mii); + } else { + // Set data for owner drawn separators. Set dwItemData NULL to indicate + // a separator. + if (owner_draw_) { + MENUITEMINFO mii; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR | MFT_OWNERDRAW; + mii.dwItemData = NULL; + SetMenuItemInfo(menu_, i, true, &mii); + } + ++sep_count; + } + } + + for (size_t i = 0; i < submenus_.size(); ++i) + submenus_[i]->SetMenuInfo(); +} + +UINT MenuWin::GetStateFlagsForItemID(int item_id) const { + // Use the delegate to get enabled and checked state. + UINT flags = + delegate()->IsCommandEnabled(item_id) ? MFS_ENABLED : MFS_DISABLED; + + if (delegate()->IsItemChecked(item_id)) + flags |= MFS_CHECKED; + + if (delegate()->IsItemDefault(item_id)) + flags |= MFS_DEFAULT; + + return flags; +} + +DWORD MenuWin::GetTPMAlignFlags() const { + // The manner in which we handle the menu alignment depends on whether or not + // the menu is displayed within a mirrored view. If the UI is mirrored, the + // alignment needs to be fliped so that instead of aligning the menu to the + // right of the point, we align it to the left and vice versa. + DWORD align_flags = TPM_TOPALIGN; + switch (anchor()) { + case TOPLEFT: + if (delegate()->IsRightToLeftUILayout()) { + align_flags |= TPM_RIGHTALIGN; + } else { + align_flags |= TPM_LEFTALIGN; + } + break; + + case TOPRIGHT: + if (delegate()->IsRightToLeftUILayout()) { + align_flags |= TPM_LEFTALIGN; + } else { + align_flags |= TPM_RIGHTALIGN; + } + break; + + default: + NOTREACHED(); + return 0; + } + return align_flags; +} + +} // namespace views diff --git a/views/controls/menu/menu_win.h b/views/controls/menu/menu_win.h new file mode 100644 index 0000000..bea4c65 --- /dev/null +++ b/views/controls/menu/menu_win.h @@ -0,0 +1,129 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CONTROLS_MENU_VIEWS_MENU_WIN_H_ +#define CONTROLS_MENU_VIEWS_MENU_WIN_H_ + +#include <vector> +#include <windows.h> + +#include "base/basictypes.h" +#include "views/controls/menu/menu.h" + +namespace views { + +namespace { +class MenuHostWindow; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Menu class +// +// A wrapper around a Win32 HMENU handle that provides convenient APIs for +// menu construction, display and subsequent command execution. +// +/////////////////////////////////////////////////////////////////////////////// +class MenuWin : public Menu { + friend class MenuHostWindow; + + public: + // Construct a Menu using the specified controller to determine command + // state. + // delegate A Menu::Delegate implementation that provides more + // information about the Menu presentation. + // anchor An alignment hint for the popup menu. + // owner The window that the menu is being brought up relative + // to. Not actually used for anything but must not be + // NULL. + MenuWin(Delegate* d, AnchorPoint anchor, HWND owner); + // Alternatively, a Menu object can be constructed wrapping an existing + // HMENU. This can be used to use the convenience methods to insert + // menu items and manage label string ownership. However this kind of + // Menu object cannot use the delegate. + explicit MenuWin(HMENU hmenu); + virtual ~MenuWin(); + + // Menu overrides. + virtual Menu* AddSubMenuWithIcon(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon); + virtual void AddSeparator(int index); + virtual void EnableMenuItemByID(int item_id, bool enabled); + virtual void EnableMenuItemAt(int index, bool enabled); + virtual void SetMenuLabel(int item_id, const std::wstring& label); + virtual bool SetIcon(const SkBitmap& icon, int item_id); + virtual void RunMenuAt(int x, int y); + virtual void Cancel(); + virtual int ItemCount(); + + protected: + virtual void AddMenuItemInternal(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon, + MenuItemType type); + + private: + // The data of menu items needed to display. + struct ItemData; + + void AddMenuItemInternal(int index, + int item_id, + const std::wstring& label, + const SkBitmap& icon, + HMENU submenu, + MenuItemType type); + + explicit MenuWin(MenuWin* parent); + + // Sets menu information before displaying, including sub-menus. + void SetMenuInfo(); + + // Get all the state flags for the |fState| field of MENUITEMINFO for the + // item with the specified id. |delegate| is consulted if non-NULL about + // the state of the item in preference to |controller_|. + UINT GetStateFlagsForItemID(int item_id) const; + + // Gets the Win32 TPM alignment flags for the specified AnchorPoint. + DWORD GetTPMAlignFlags() const; + + // The Win32 Menu Handle we wrap + HMENU menu_; + + // The window that would receive WM_COMMAND messages when the user selects + // an item from the menu. + HWND owner_; + + // This list is used to store the default labels for the menu items. + // We may use contextual labels when RunMenu is called, so we must save + // a copy of default ones here. + std::vector<std::wstring> labels_; + + // A flag to indicate whether this menu will be drawn by the Menu class. + // If it's true, all the menu items will be owner drawn. Otherwise, + // all the drawing will be done by Windows. + bool owner_draw_; + + // This list is to store the string labels and icons to display. It's used + // when owner_draw_ is true. We give MENUITEMINFO pointers to these + // structures to specify what we'd like to draw. If owner_draw_ is false, + // we only give MENUITEMINFO pointers to the labels_. + // The label member of the ItemData structure comes from either labels_ or + // the GetContextualLabel. + std::vector<ItemData*> item_data_; + + // Our sub-menus, if any. + std::vector<MenuWin*> submenus_; + + // Whether the menu is visible. + bool is_menu_visible_; + + DISALLOW_COPY_AND_ASSIGN(MenuWin); +}; + +} // namespace views + +#endif // CONTROLS_MENU_VIEWS_MENU_WIN_H_ diff --git a/views/controls/scrollbar/bitmap_scroll_bar.cc b/views/controls/scrollbar/bitmap_scroll_bar.cc index 18096fb..4fe8934 100644 --- a/views/controls/scrollbar/bitmap_scroll_bar.cc +++ b/views/controls/scrollbar/bitmap_scroll_bar.cc @@ -540,18 +540,19 @@ void BitmapScrollBar::ShowContextMenu(View* source, View::ConvertPointFromWidget(this, &temp_pt); context_menu_mouse_position_ = IsHorizontal() ? temp_pt.x() : temp_pt.y(); - Menu menu(this, Menu::TOPLEFT, GetWidget()->GetNativeView()); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollHere); - menu.AppendSeparator(); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollStart); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollEnd); - menu.AppendSeparator(); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageUp); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageDown); - menu.AppendSeparator(); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev); - menu.AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext); - menu.RunMenuAt(x, y); + scoped_ptr<Menu> menu( + Menu::Create(this, Menu::TOPLEFT, GetWidget()->GetNativeView())); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollHere); + menu->AppendSeparator(); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollStart); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollEnd); + menu->AppendSeparator(); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageUp); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageDown); + menu->AppendSeparator(); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev); + menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext); + menu->RunMenuAt(x, y); } /////////////////////////////////////////////////////////////////////////////// diff --git a/views/controls/text_field.cc b/views/controls/text_field.cc index 08f982a..62762f4 100644 --- a/views/controls/text_field.cc +++ b/views/controls/text_field.cc @@ -24,7 +24,7 @@ #include "grit/generated_resources.h" #include "skia/ext/skia_utils_win.h" #include "views/controls/hwnd_view.h" -#include "views/controls/menu/menu.h" +#include "views/controls/menu/menu_win.h" #include "views/focus/focus_util_win.h" #include "views/views_delegate.h" #include "views/widget/widget.h" @@ -289,7 +289,7 @@ TextField::Edit::Edit(TextField* parent, bool draw_border) ole_interface.Attach(GetOleInterface()); text_object_model_ = ole_interface; - context_menu_.reset(new Menu(this, Menu::TOPLEFT, m_hWnd)); + context_menu_.reset(new MenuWin(this, Menu::TOPLEFT, m_hWnd)); context_menu_->AppendMenuItemWithLabel(IDS_UNDO, l10n_util::GetString(IDS_UNDO)); context_menu_->AppendSeparator(); diff --git a/views/views.vcproj b/views/views.vcproj index 0c336a6..bdc83a5 100644 --- a/views/views.vcproj +++ b/views/views.vcproj @@ -768,6 +768,14 @@ > </File> <File + RelativePath=".\controls\menu\menu_win.cc" + > + </File> + <File + RelativePath=".\controls\menu\menu_win.h" + > + </File> + <File RelativePath=".\controls\menu\view_menu_delegate.h" > </File> |