summaryrefslogtreecommitdiffstats
path: root/views/controls/menu
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-24 15:46:45 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-24 15:46:45 +0000
commita87ecb3c7170caa40810def524fbae1aee4d3533 (patch)
tree947732a745f124feda7ace8ac466cf75c746db62 /views/controls/menu
parentde1b764f404e5fb6975e7cc9de7120d46e28dcfd (diff)
downloadchromium_src-a87ecb3c7170caa40810def524fbae1aee4d3533.zip
chromium_src-a87ecb3c7170caa40810def524fbae1aee4d3533.tar.gz
chromium_src-a87ecb3c7170caa40810def524fbae1aee4d3533.tar.bz2
Further refactoring of menus for GTK. I've now separated out
everything and menus some what work on GTK. I need to get painting working and I'm sure there will be fine tuning, but I want to check in all this splitting of files before someone else needs change one of these files. Thankfully I wrote an interactive ui test that exercises much of this code, and it still passes:) BUG=none TEST=thoroughly test bookmark menus on windows to make sure I didn't break them. Review URL: http://codereview.chromium.org/174274 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24098 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/controls/menu')
-rw-r--r--views/controls/menu/chrome_menu.cc2833
-rw-r--r--views/controls/menu/chrome_menu.h962
-rw-r--r--views/controls/menu/menu_config.cc25
-rw-r--r--views/controls/menu/menu_config.h102
-rw-r--r--views/controls/menu/menu_config_gtk.cc15
-rw-r--r--views/controls/menu/menu_config_win.cc84
-rw-r--r--views/controls/menu/menu_controller.cc48
-rw-r--r--views/controls/menu/menu_controller.h17
-rw-r--r--views/controls/menu/menu_host_gtk.cc99
-rw-r--r--views/controls/menu/menu_host_gtk.h51
-rw-r--r--views/controls/menu/menu_host_root_view.cc92
-rw-r--r--views/controls/menu/menu_host_root_view.h56
-rw-r--r--views/controls/menu/menu_host_win.cc114
-rw-r--r--views/controls/menu/menu_host_win.h55
-rw-r--r--views/controls/menu/menu_item_view.cc385
-rw-r--r--views/controls/menu/menu_item_view.h26
-rw-r--r--views/controls/menu/menu_item_view_gtk.cc16
-rw-r--r--views/controls/menu/menu_item_view_win.cc143
-rw-r--r--views/controls/menu/menu_scroll_view_container.cc28
-rw-r--r--views/controls/menu/menu_separator.h27
-rw-r--r--views/controls/menu/menu_separator_gtk.cc21
-rw-r--r--views/controls/menu/menu_separator_win.cc47
-rw-r--r--views/controls/menu/submenu_view.cc243
23 files changed, 1086 insertions, 4403 deletions
diff --git a/views/controls/menu/chrome_menu.cc b/views/controls/menu/chrome_menu.cc
deleted file mode 100644
index 351e082..0000000
--- a/views/controls/menu/chrome_menu.cc
+++ /dev/null
@@ -1,2833 +0,0 @@
-// 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/chrome_menu.h"
-
-#include <windows.h>
-#include <uxtheme.h>
-#include <Vssym32.h>
-
-#include "app/gfx/canvas.h"
-#include "app/gfx/color_utils.h"
-#include "app/l10n_util.h"
-#include "app/l10n_util_win.h"
-#include "app/os_exchange_data.h"
-#include "app/os_exchange_data_provider_win.h"
-#include "base/base_drag_source.h"
-#include "base/gfx/native_theme.h"
-#include "base/message_loop.h"
-#include "base/task.h"
-#include "base/timer.h"
-#include "base/win_util.h"
-#include "grit/app_strings.h"
-#include "skia/ext/skia_utils_win.h"
-#include "views/border.h"
-#include "views/drag_utils.h"
-#include "views/focus/focus_manager.h"
-#include "views/view_constants.h"
-#include "views/widget/root_view.h"
-#include "views/widget/widget_win.h"
-
-#undef min
-#undef max
-
-using base::Time;
-using base::TimeDelta;
-
-// 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 used if the menu doesn't have icons.
-static const int kItemNoIconTopMargin = 1;
-static const int kItemNoIconBottomMargin = 3;
-
-// Margins between the left of the item and the icon.
-static const int kItemLeftMargin = 4;
-
-// Padding between the label and submenu arrow.
-static const int kLabelToArrowPadding = 10;
-
-// Padding between the arrow and the edge.
-static const int kArrowToEdgePadding = 5;
-
-// Padding between the icon and label.
-static const int kIconToLabelPadding = 8;
-
-// Padding between the gutter and label.
-static const int kGutterToLabel = 5;
-
-// Height of the scroll arrow.
-// This goes up to 4 with large fonts, but this is close enough for now.
-static const int kScrollArrowHeight = 3;
-
-// Size of the check. This comes from the OS.
-static int check_width;
-static int check_height;
-
-// Size of the submenu arrow. This comes from the OS.
-static int arrow_width;
-static int arrow_height;
-
-// Width of the gutter. Only used if render_gutter is true.
-static int gutter_width;
-
-// Margins between the right of the item and the label.
-static int item_right_margin;
-
-// X-coordinate of where the label starts.
-static int label_start;
-
-// Height of the separator.
-static int separator_height;
-
-// Padding around the edges of the submenu.
-static const int kSubmenuBorderSize = 3;
-
-// Amount to inset submenus.
-static const int kSubmenuHorizontalInset = 3;
-
-// Delay, in ms, between when menus are selected are moused over and the menu
-// appears.
-static const int kShowDelay = 400;
-
-// Amount of time from when the drop exits the menu and the menu is hidden.
-static const int kCloseOnExitTime = 1200;
-
-// Height of the drop indicator. This should be an event number.
-static const int kDropIndicatorHeight = 2;
-
-// Color of the drop indicator.
-static const SkColor kDropIndicatorColor = SK_ColorBLACK;
-
-// Whether or not the gutter should be rendered. The gutter is specific to
-// Vista.
-static bool render_gutter = false;
-
-// Max width of a menu. There does not appear to be an OS value for this, yet
-// both IE and FF restrict the max width of a menu.
-static const int kMaxMenuWidth = 400;
-
-// Period of the scroll timer (in milliseconds).
-static const int kScrollTimerMS = 30;
-
-// Preferred height of menu items. Reset every time a menu is run.
-static int pref_menu_height;
-
-// Are mnemonics shown? This is updated before the menus are shown.
-static bool show_mnemonics;
-
-using gfx::NativeTheme;
-
-namespace views {
-
-namespace {
-
-// Returns the font menus are to use.
-gfx::Font GetMenuFont() {
- NONCLIENTMETRICS metrics;
- win_util::GetNonClientMetrics(&metrics);
-
- l10n_util::AdjustUIFont(&(metrics.lfMenuFont));
- HFONT font = CreateFontIndirect(&metrics.lfMenuFont);
- DLOG_ASSERT(font);
- return gfx::Font::CreateFont(font);
-}
-
-// Calculates all sizes that we can from the OS.
-//
-// This is invoked prior to Running a menu.
-void UpdateMenuPartSizes(bool has_icons) {
- HDC dc = GetDC(NULL);
- RECT bounds = { 0, 0, 200, 200 };
- SIZE check_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, &bounds,
- TS_TRUE, &check_size) == S_OK) {
- check_width = check_size.cx;
- check_height = check_size.cy;
- } else {
- check_width = GetSystemMetrics(SM_CXMENUCHECK);
- check_height = GetSystemMetrics(SM_CYMENUCHECK);
- }
-
- SIZE arrow_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPSUBMENU, MSM_NORMAL, &bounds,
- TS_TRUE, &arrow_size) == S_OK) {
- arrow_width = arrow_size.cx;
- arrow_height = arrow_size.cy;
- } else {
- // Sadly I didn't see a specify metrics for this.
- arrow_width = GetSystemMetrics(SM_CXMENUCHECK);
- arrow_height = GetSystemMetrics(SM_CYMENUCHECK);
- }
-
- SIZE gutter_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPGUTTER, MSM_NORMAL, &bounds,
- TS_TRUE, &gutter_size) == S_OK) {
- gutter_width = gutter_size.cx;
- render_gutter = true;
- } else {
- gutter_width = 0;
- render_gutter = false;
- }
-
- SIZE separator_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPSEPARATOR, MSM_NORMAL, &bounds,
- TS_TRUE, &separator_size) == S_OK) {
- separator_height = separator_size.cy;
- } else {
- separator_height = GetSystemMetrics(SM_CYMENU) / 2;
- }
-
- item_right_margin = kLabelToArrowPadding + arrow_width + kArrowToEdgePadding;
-
- if (has_icons) {
- label_start = kItemLeftMargin + check_width + kIconToLabelPadding;
- } else {
- // If there are no icons don't pad by the icon to label padding. This
- // makes us look close to system menus.
- label_start = kItemLeftMargin + check_width;
- }
- if (render_gutter)
- label_start += gutter_width + kGutterToLabel;
-
- ReleaseDC(NULL, dc);
-
- MenuItemView menu_item(NULL);
- menu_item.SetTitle(L"blah"); // Text doesn't matter here.
- pref_menu_height = menu_item.GetPreferredSize().height();
-}
-
-// Convenience for scrolling the view such that the origin is visible.
-static void ScrollToVisible(View* view) {
- view->ScrollRectToVisible(0, 0, view->width(), view->height());
-}
-
-} // namespace
-
-// MenuScrollTask --------------------------------------------------------------
-
-// MenuScrollTask is used when the SubmenuView does not all fit on screen and
-// the mouse is over the scroll up/down buttons. MenuScrollTask schedules
-// itself with a RepeatingTimer. When Run is invoked MenuScrollTask scrolls
-// appropriately.
-
-class MenuController::MenuScrollTask {
- public:
- MenuScrollTask() : submenu_(NULL) {
- pixels_per_second_ = pref_menu_height * 20;
- }
-
- void Update(const MenuController::MenuPart& part) {
- if (!part.is_scroll()) {
- StopScrolling();
- return;
- }
- DCHECK(part.submenu);
- SubmenuView* new_menu = part.submenu;
- bool new_is_up = (part.type == MenuController::MenuPart::SCROLL_UP);
- if (new_menu == submenu_ && is_scrolling_up_ == new_is_up)
- return;
-
- start_scroll_time_ = Time::Now();
- start_y_ = part.submenu->GetVisibleBounds().y();
- submenu_ = new_menu;
- is_scrolling_up_ = new_is_up;
-
- if (!scrolling_timer_.IsRunning()) {
- scrolling_timer_.Start(TimeDelta::FromMilliseconds(kScrollTimerMS), this,
- &MenuScrollTask::Run);
- }
- }
-
- void StopScrolling() {
- if (scrolling_timer_.IsRunning()) {
- scrolling_timer_.Stop();
- submenu_ = NULL;
- }
- }
-
- // The menu being scrolled. Returns null if not scrolling.
- SubmenuView* submenu() const { return submenu_; }
-
- private:
- void Run() {
- DCHECK(submenu_);
- gfx::Rect vis_rect = submenu_->GetVisibleBounds();
- const int delta_y = static_cast<int>(
- (Time::Now() - start_scroll_time_).InMilliseconds() *
- pixels_per_second_ / 1000);
- int target_y = start_y_;
- if (is_scrolling_up_)
- target_y = std::max(0, target_y - delta_y);
- else
- target_y = std::min(submenu_->height() - vis_rect.height(),
- target_y + delta_y);
- submenu_->ScrollRectToVisible(vis_rect.x(), target_y, vis_rect.width(),
- vis_rect.height());
- }
-
- // SubmenuView being scrolled.
- SubmenuView* submenu_;
-
- // Direction scrolling.
- bool is_scrolling_up_;
-
- // Timer to periodically scroll.
- base::RepeatingTimer<MenuScrollTask> scrolling_timer_;
-
- // Time we started scrolling at.
- Time start_scroll_time_;
-
- // How many pixels to scroll per second.
- int pixels_per_second_;
-
- // Y-coordinate of submenu_view_ when scrolling started.
- int start_y_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuScrollTask);
-};
-
-namespace {
-
-// MenuScrollButton ------------------------------------------------------------
-
-// MenuScrollButton is used for the scroll buttons when not all menu items fit
-// on screen. MenuScrollButton forwards appropriate events to the
-// MenuController.
-
-class MenuScrollButton : public View {
- public:
- explicit MenuScrollButton(SubmenuView* host, bool is_up)
- : host_(host),
- is_up_(is_up),
- // Make our height the same as that of other MenuItemViews.
- pref_height_(pref_menu_height) {
- }
-
- virtual gfx::Size GetPreferredSize() {
- return gfx::Size(kScrollArrowHeight * 2 - 1, pref_height_);
- }
-
- virtual bool CanDrop(const OSExchangeData& data) {
- DCHECK(host_->GetMenuItem()->GetMenuController());
- return true; // Always return true so that drop events are targeted to us.
- }
-
- virtual void OnDragEntered(const DropTargetEvent& event) {
- DCHECK(host_->GetMenuItem()->GetMenuController());
- host_->GetMenuItem()->GetMenuController()->OnDragEnteredScrollButton(
- host_, is_up_);
- }
-
- virtual int OnDragUpdated(const DropTargetEvent& event) {
- return DragDropTypes::DRAG_NONE;
- }
-
- virtual void OnDragExited() {
- DCHECK(host_->GetMenuItem()->GetMenuController());
- host_->GetMenuItem()->GetMenuController()->OnDragExitedScrollButton(host_);
- }
-
- virtual int OnPerformDrop(const DropTargetEvent& event) {
- return DragDropTypes::DRAG_NONE;
- }
-
- virtual void Paint(gfx::Canvas* canvas) {
- HDC dc = canvas->beginPlatformPaint();
-
- // The background.
- RECT item_bounds = { 0, 0, width(), height() };
- NativeTheme::instance()->PaintMenuItemBackground(
- NativeTheme::MENU, dc, MENU_POPUPITEM, MPI_NORMAL, false,
- &item_bounds);
-
- // Then the arrow.
- int x = width() / 2;
- int y = (height() - kScrollArrowHeight) / 2;
- int delta_y = 1;
- if (!is_up_) {
- delta_y = -1;
- y += kScrollArrowHeight;
- }
- SkColor arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT);
- for (int i = 0; i < kScrollArrowHeight; ++i, --x, y += delta_y)
- canvas->FillRectInt(arrow_color, x, y, (i * 2) + 1, 1);
-
- canvas->endPlatformPaint();
- }
-
- private:
- // SubmenuView we were created for.
- SubmenuView* host_;
-
- // Direction of the button.
- bool is_up_;
-
- // Preferred height.
- int pref_height_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuScrollButton);
-};
-
-// MenuScrollView --------------------------------------------------------------
-
-// MenuScrollView is a viewport for the SubmenuView. It's reason to exist is so
-// that ScrollRectToVisible works.
-//
-// NOTE: It is possible to use ScrollView directly (after making it deal with
-// null scrollbars), but clicking on a child of ScrollView forces the window to
-// become active, which we don't want. As we really only need a fraction of
-// what ScrollView does, we use a one off variant.
-
-class MenuScrollView : public View {
- public:
- explicit MenuScrollView(View* child) {
- AddChildView(child);
- }
-
- virtual void ScrollRectToVisible(int x, int y, int width, int height) {
- // NOTE: this assumes we only want to scroll in the y direction.
-
- View* child = GetContents();
- // Convert y to view's coordinates.
- y -= child->y();
- gfx::Size pref = child->GetPreferredSize();
- // Constrain y to make sure we don't show past the bottom of the view.
- y = std::max(0, std::min(pref.height() - this->height(), y));
- child->SetY(-y);
- }
-
- // Returns the contents, which is the SubmenuView.
- View* GetContents() {
- return GetChildViewAt(0);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MenuScrollView);
-};
-
-} // namespace
-
-// MenuScrollViewContainer -----------------------------------------------------
-
-// MenuScrollViewContainer contains the SubmenuView (through a MenuScrollView)
-// and two scroll buttons. The scroll buttons are only visible and enabled if
-// the preferred height of the SubmenuView is bigger than our bounds.
-class MenuScrollViewContainer : public View {
- public:
- explicit MenuScrollViewContainer(SubmenuView* content_view) {
- scroll_up_button_ = new MenuScrollButton(content_view, true);
- scroll_down_button_ = new MenuScrollButton(content_view, false);
- AddChildView(scroll_up_button_);
- AddChildView(scroll_down_button_);
-
- scroll_view_ = new MenuScrollView(content_view);
- AddChildView(scroll_view_);
-
- set_border(Border::CreateEmptyBorder(
- kSubmenuBorderSize, kSubmenuBorderSize,
- kSubmenuBorderSize, kSubmenuBorderSize));
- }
-
- virtual void Paint(gfx::Canvas* canvas) {
- HDC dc = canvas->beginPlatformPaint();
- CRect bounds(0, 0, width(), height());
- NativeTheme::instance()->PaintMenuBackground(
- NativeTheme::MENU, dc, MENU_POPUPBACKGROUND, 0, &bounds);
- canvas->endPlatformPaint();
- }
-
- View* scroll_down_button() { return scroll_down_button_; }
-
- View* scroll_up_button() { return scroll_up_button_; }
-
- virtual void Layout() {
- gfx::Insets insets = GetInsets();
- int x = insets.left();
- int y = insets.top();
- int width = View::width() - insets.width();
- int content_height = height() - insets.height();
- if (!scroll_up_button_->IsVisible()) {
- scroll_view_->SetBounds(x, y, width, content_height);
- scroll_view_->Layout();
- return;
- }
-
- gfx::Size pref = scroll_up_button_->GetPreferredSize();
- scroll_up_button_->SetBounds(x, y, width, pref.height());
- content_height -= pref.height();
-
- const int scroll_view_y = y + pref.height();
-
- pref = scroll_down_button_->GetPreferredSize();
- scroll_down_button_->SetBounds(x, height() - pref.height() - insets.top(),
- width, pref.height());
- content_height -= pref.height();
-
- scroll_view_->SetBounds(x, scroll_view_y, width, content_height);
- scroll_view_->Layout();
- }
-
- virtual void DidChangeBounds(const gfx::Rect& previous,
- const gfx::Rect& current) {
- gfx::Size content_pref = scroll_view_->GetContents()->GetPreferredSize();
- scroll_up_button_->SetVisible(content_pref.height() > height());
- scroll_down_button_->SetVisible(content_pref.height() > height());
- }
-
- virtual gfx::Size GetPreferredSize() {
- gfx::Size prefsize = scroll_view_->GetContents()->GetPreferredSize();
- gfx::Insets insets = GetInsets();
- prefsize.Enlarge(insets.width(), insets.height());
- return prefsize;
- }
-
- private:
- // The scroll buttons.
- View* scroll_up_button_;
- View* scroll_down_button_;
-
- // The scroll view.
- MenuScrollView* scroll_view_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuScrollViewContainer);
-};
-
-namespace {
-
-// MenuSeparator ---------------------------------------------------------------
-
-// Renders a separator.
-
-class MenuSeparator : public View {
- public:
- MenuSeparator() {
- }
-
- void Paint(gfx::Canvas* canvas) {
- // The gutter is rendered before the background.
- int start_x = 0;
- int start_y = height() / 3;
- HDC dc = canvas->beginPlatformPaint();
- if (render_gutter) {
- // If render_gutter is true, we're on Vista and need to render the
- // gutter, then indent the separator from the gutter.
- RECT gutter_bounds = { label_start - kGutterToLabel - gutter_width, 0, 0,
- height() };
- gutter_bounds.right = gutter_bounds.left + gutter_width;
- NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER, MPI_NORMAL,
- &gutter_bounds);
- start_x = gutter_bounds.left + gutter_width;
- start_y = 0;
- }
- RECT separator_bounds = { start_x, start_y, width(), height() };
- NativeTheme::instance()->PaintMenuSeparator(dc, MENU_POPUPSEPARATOR,
- MPI_NORMAL, &separator_bounds);
- canvas->endPlatformPaint();
- }
-
- gfx::Size GetPreferredSize() {
- return gfx::Size(10, // Just in case we're the only item in a menu.
- separator_height);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MenuSeparator);
-};
-
-// MenuHostRootView ----------------------------------------------------------
-
-// MenuHostRootView is the RootView of the window showing the menu.
-// SubmenuView's scroll view is added as a child of MenuHostRootView.
-// MenuHostRootView forwards relevant events to the MenuController.
-//
-// As all the menu items are owned by the root menu item, care must be taken
-// such that when MenuHostRootView is deleted it doesn't delete the menu items.
-
-class MenuHostRootView : public RootView {
- public:
- explicit MenuHostRootView(Widget* widget,
- SubmenuView* submenu)
- : RootView(widget),
- submenu_(submenu),
- forward_drag_to_menu_controller_(true),
- suspend_events_(false) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << " new MenuHostRootView " << this;
-#endif
- }
-
- virtual bool OnMousePressed(const MouseEvent& event) {
- if (suspend_events_)
- return true;
-
- forward_drag_to_menu_controller_ =
- ((event.x() < 0 || event.y() < 0 || event.x() >= width() ||
- event.y() >= height()) ||
- !RootView::OnMousePressed(event));
- if (forward_drag_to_menu_controller_)
- GetMenuController()->OnMousePressed(submenu_, event);
- return true;
- }
-
- virtual bool OnMouseDragged(const MouseEvent& event) {
- if (suspend_events_)
- return true;
-
- if (forward_drag_to_menu_controller_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << " MenuHostRootView::OnMouseDragged source=" << submenu_;
-#endif
- GetMenuController()->OnMouseDragged(submenu_, event);
- return true;
- }
- return RootView::OnMouseDragged(event);
- }
-
- virtual void OnMouseReleased(const MouseEvent& event, bool canceled) {
- if (suspend_events_)
- return;
-
- RootView::OnMouseReleased(event, canceled);
- if (forward_drag_to_menu_controller_) {
- forward_drag_to_menu_controller_ = false;
- if (canceled) {
- GetMenuController()->Cancel(true);
- } else {
- GetMenuController()->OnMouseReleased(submenu_, event);
- }
- }
- }
-
- virtual void OnMouseMoved(const MouseEvent& event) {
- if (suspend_events_)
- return;
-
- RootView::OnMouseMoved(event);
- GetMenuController()->OnMouseMoved(submenu_, event);
- }
-
- virtual void ProcessOnMouseExited() {
- if (suspend_events_)
- return;
-
- RootView::ProcessOnMouseExited();
- }
-
- virtual bool ProcessMouseWheelEvent(const MouseWheelEvent& e) {
- // RootView::ProcessMouseWheelEvent forwards to the focused view. We don't
- // have a focused view, so we need to override this then forward to
- // the menu.
- return submenu_->OnMouseWheel(e);
- }
-
- void SuspendEvents() {
- suspend_events_ = true;
- }
-
- private:
- MenuController* GetMenuController() {
- return submenu_->GetMenuItem()->GetMenuController();
- }
-
- // The SubmenuView we contain.
- SubmenuView* submenu_;
-
- // Whether mouse dragged/released should be forwarded to the MenuController.
- bool forward_drag_to_menu_controller_;
-
- // Whether events are suspended. If true, no events are forwarded to the
- // MenuController.
- bool suspend_events_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuHostRootView);
-};
-
-} // namespace
-
-// MenuHost ------------------------------------------------------------------
-
-// MenuHost is the window responsible for showing a single menu.
-//
-// Similar to MenuHostRootView, care must be taken such that when MenuHost is
-// deleted, it doesn't delete the menu items. MenuHost is closed via a
-// DelayedClosed, which avoids timing issues with deleting the window while
-// capture or events are directed at it.
-
-class MenuHost : public WidgetWin {
- public:
- explicit MenuHost(SubmenuView* submenu)
- : closed_(false),
- submenu_(submenu),
- owns_capture_(false) {
- set_window_style(WS_POPUP);
- set_initial_class_style(
- (win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
- 0 : CS_DROPSHADOW);
- is_mouse_down_ =
- ((GetKeyState(VK_LBUTTON) & 0x80) ||
- (GetKeyState(VK_RBUTTON) & 0x80) ||
- (GetKeyState(VK_MBUTTON) & 0x80) ||
- (GetKeyState(VK_XBUTTON1) & 0x80) ||
- (GetKeyState(VK_XBUTTON2) & 0x80));
- // Mouse clicks shouldn't give us focus.
- set_window_ex_style(WS_EX_TOPMOST | WS_EX_NOACTIVATE);
- }
-
- void Init(HWND parent,
- const gfx::Rect& bounds,
- View* contents_view,
- bool do_capture) {
- WidgetWin::Init(parent, bounds);
- SetContentsView(contents_view);
- // We don't want to take focus away from the hosting window.
- ShowWindow(SW_SHOWNA);
- owns_capture_ = do_capture;
- if (do_capture) {
- SetCapture();
- has_capture_ = true;
-#ifdef DEBUG_MENU
- DLOG(INFO) << "Doing capture";
-#endif
- }
- }
-
- virtual void Hide() {
- if (closed_) {
- // We're already closed, nothing to do.
- // This is invoked twice if the first time just hid us, and the second
- // time deleted Closed (deleted) us.
- return;
- }
- // The menus are freed separately, and possibly before the window is closed,
- // remove them so that View doesn't try to access deleted objects.
- static_cast<MenuHostRootView*>(GetRootView())->SuspendEvents();
- GetRootView()->RemoveAllChildViews(false);
- closed_ = true;
- ReleaseCapture();
- WidgetWin::Hide();
- }
-
- virtual void HideWindow() {
- // Make sure we release capture before hiding.
- ReleaseCapture();
- WidgetWin::Hide();
- }
-
- virtual void OnCaptureChanged(HWND hwnd) {
- WidgetWin::OnCaptureChanged(hwnd);
- owns_capture_ = false;
-#ifdef DEBUG_MENU
- DLOG(INFO) << "Capture changed";
-#endif
- }
-
- void ReleaseCapture() {
- if (owns_capture_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "released capture";
-#endif
- owns_capture_ = false;
- ::ReleaseCapture();
- }
- }
-
- protected:
- // Overriden to create MenuHostRootView.
- virtual RootView* CreateRootView() {
- return new MenuHostRootView(this, submenu_);
- }
-
- virtual void OnCancelMode() {
- if (!closed_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnCanceMode, closing menu";
-#endif
- submenu_->GetMenuItem()->GetMenuController()->Cancel(true);
- }
- }
-
- // Overriden to return false, we do NOT want to release capture on mouse
- // release.
- virtual bool ReleaseCaptureOnMouseReleased() {
- return false;
- }
-
- private:
- // If true, we've been closed.
- bool closed_;
-
- // If true, we own the capture and need to release it.
- bool owns_capture_;
-
- // The view we contain.
- SubmenuView* submenu_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuHost);
-};
-
-namespace {
-
-// EmptyMenuMenuItem ---------------------------------------------------------
-
-// EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem
-// is itself a MenuItemView, but it uses a different ID so that it isn't
-// identified as a MenuItemView.
-
-class EmptyMenuMenuItem : public MenuItemView {
- public:
- // ID used for EmptyMenuMenuItem.
- static const int kEmptyMenuItemViewID;
-
- explicit EmptyMenuMenuItem(MenuItemView* parent) :
- MenuItemView(parent, 0, NORMAL) {
- SetTitle(l10n_util::GetString(IDS_APP_MENU_EMPTY_SUBMENU));
- // Set this so that we're not identified as a normal menu item.
- SetID(kEmptyMenuItemViewID);
- SetEnabled(false);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(EmptyMenuMenuItem);
-};
-
-// static
-const int EmptyMenuMenuItem::kEmptyMenuItemViewID =
- MenuItemView::kMenuItemViewID + 1;
-
-} // namespace
-
-// SubmenuView ---------------------------------------------------------------
-
-SubmenuView::SubmenuView(MenuItemView* parent)
- : parent_menu_item_(parent),
- host_(NULL),
- drop_item_(NULL),
- drop_position_(MenuDelegate::DROP_NONE),
- scroll_view_container_(NULL) {
- DCHECK(parent);
- // We'll delete ourselves, otherwise the ScrollView would delete us on close.
- SetParentOwned(false);
-}
-
-SubmenuView::~SubmenuView() {
- // The menu may not have been closed yet (it will be hidden, but not
- // necessarily closed).
- Close();
-
- delete scroll_view_container_;
-}
-
-int SubmenuView::GetMenuItemCount() {
- int count = 0;
- for (int i = 0; i < GetChildViewCount(); ++i) {
- if (GetChildViewAt(i)->GetID() == MenuItemView::kMenuItemViewID)
- count++;
- }
- return count;
-}
-
-MenuItemView* SubmenuView::GetMenuItemAt(int index) {
- for (int i = 0, count = 0; i < GetChildViewCount(); ++i) {
- if (GetChildViewAt(i)->GetID() == MenuItemView::kMenuItemViewID &&
- count++ == index) {
- return static_cast<MenuItemView*>(GetChildViewAt(i));
- }
- }
- NOTREACHED();
- return NULL;
-}
-
-void SubmenuView::Layout() {
- // We're in a ScrollView, and need to set our width/height ourselves.
- View* parent = GetParent();
- if (!parent)
- return;
- SetBounds(x(), y(), parent->width(), GetPreferredSize().height());
-
- gfx::Insets insets = GetInsets();
- int x = insets.left();
- int y = insets.top();
- int menu_item_width = width() - insets.width();
- for (int i = 0; i < GetChildViewCount(); ++i) {
- View* child = GetChildViewAt(i);
- gfx::Size child_pref_size = child->GetPreferredSize();
- child->SetBounds(x, y, menu_item_width, child_pref_size.height());
- y += child_pref_size.height();
- }
-}
-
-gfx::Size SubmenuView::GetPreferredSize() {
- if (GetChildViewCount() == 0)
- return gfx::Size();
-
- int max_width = 0;
- int height = 0;
- for (int i = 0; i < GetChildViewCount(); ++i) {
- View* child = GetChildViewAt(i);
- gfx::Size child_pref_size = child->GetPreferredSize();
- max_width = std::max(max_width, child_pref_size.width());
- height += child_pref_size.height();
- }
- gfx::Insets insets = GetInsets();
- return gfx::Size(max_width + insets.width(), height + insets.height());
-}
-
-void SubmenuView::DidChangeBounds(const gfx::Rect& previous,
- const gfx::Rect& current) {
- SchedulePaint();
-}
-
-void SubmenuView::PaintChildren(gfx::Canvas* canvas) {
- View::PaintChildren(canvas);
-
- if (drop_item_ && drop_position_ != MenuDelegate::DROP_ON)
- PaintDropIndicator(canvas, drop_item_, drop_position_);
-}
-
-// TODO(sky): need to add support for new dnd methods for Linux.
-
-bool SubmenuView::CanDrop(const OSExchangeData& data) {
- DCHECK(GetMenuItem()->GetMenuController());
- return GetMenuItem()->GetMenuController()->CanDrop(this, data);
-}
-
-void SubmenuView::OnDragEntered(const DropTargetEvent& event) {
- DCHECK(GetMenuItem()->GetMenuController());
- GetMenuItem()->GetMenuController()->OnDragEntered(this, event);
-}
-
-int SubmenuView::OnDragUpdated(const DropTargetEvent& event) {
- DCHECK(GetMenuItem()->GetMenuController());
- return GetMenuItem()->GetMenuController()->OnDragUpdated(this, event);
-}
-
-void SubmenuView::OnDragExited() {
- DCHECK(GetMenuItem()->GetMenuController());
- GetMenuItem()->GetMenuController()->OnDragExited(this);
-}
-
-int SubmenuView::OnPerformDrop(const DropTargetEvent& event) {
- DCHECK(GetMenuItem()->GetMenuController());
- return GetMenuItem()->GetMenuController()->OnPerformDrop(this, event);
-}
-
-bool SubmenuView::OnMouseWheel(const MouseWheelEvent& e) {
- gfx::Rect vis_bounds = GetVisibleBounds();
- int menu_item_count = GetMenuItemCount();
- if (vis_bounds.height() == height() || !menu_item_count) {
- // All menu items are visible, nothing to scroll.
- return true;
- }
-
- // Find the index of the first menu item whose y-coordinate is >= visible
- // y-coordinate.
- int first_vis_index = -1;
- for (int i = 0; i < menu_item_count; ++i) {
- MenuItemView* menu_item = GetMenuItemAt(i);
- if (menu_item->y() == vis_bounds.y()) {
- first_vis_index = i;
- break;
- } else if (menu_item->y() > vis_bounds.y()) {
- first_vis_index = std::max(0, i - 1);
- break;
- }
- }
- if (first_vis_index == -1)
- return true;
-
- // If the first item isn't entirely visible, make it visible, otherwise make
- // the next/previous one entirely visible.
- int delta = abs(e.GetOffset() / WHEEL_DELTA);
- bool scroll_up = (e.GetOffset() > 0);
- while (delta-- > 0) {
- int scroll_amount = 0;
- if (scroll_up) {
- if (GetMenuItemAt(first_vis_index)->y() == vis_bounds.y()) {
- if (first_vis_index != 0) {
- scroll_amount = GetMenuItemAt(first_vis_index - 1)->y() -
- vis_bounds.y();
- first_vis_index--;
- } else {
- break;
- }
- } else {
- scroll_amount = GetMenuItemAt(first_vis_index)->y() - vis_bounds.y();
- }
- } else {
- if (first_vis_index + 1 == GetMenuItemCount())
- break;
- scroll_amount = GetMenuItemAt(first_vis_index + 1)->y() -
- vis_bounds.y();
- if (GetMenuItemAt(first_vis_index)->y() == vis_bounds.y())
- first_vis_index++;
- }
- ScrollRectToVisible(0, vis_bounds.y() + scroll_amount, vis_bounds.width(),
- vis_bounds.height());
- vis_bounds = GetVisibleBounds();
- }
-
- return true;
-}
-
-bool SubmenuView::IsShowing() {
- return host_ && host_->IsVisible();
-}
-
-void SubmenuView::ShowAt(HWND parent,
- const gfx::Rect& bounds,
- bool do_capture) {
- if (host_) {
- host_->ShowWindow(SW_SHOWNA);
- return;
- }
-
- host_ = new MenuHost(this);
- // Force construction of the scroll view container.
- GetScrollViewContainer();
- // Make sure the first row is visible.
- ScrollRectToVisible(0, 0, 1, 1);
- host_->Init(parent, bounds, scroll_view_container_, do_capture);
-}
-
-void SubmenuView::Close() {
- if (host_) {
- host_->Close();
- host_ = NULL;
- }
-}
-
-void SubmenuView::Hide() {
- if (host_)
- host_->HideWindow();
-}
-
-void SubmenuView::ReleaseCapture() {
- host_->ReleaseCapture();
-}
-
-bool SubmenuView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) {
- return views::FocusManager::IsTabTraversalKeyEvent(e);
-}
-
-void SubmenuView::SetDropMenuItem(MenuItemView* item,
- MenuDelegate::DropPosition position) {
- if (drop_item_ == item && drop_position_ == position)
- return;
- SchedulePaintForDropIndicator(drop_item_, drop_position_);
- drop_item_ = item;
- drop_position_ = position;
- SchedulePaintForDropIndicator(drop_item_, drop_position_);
-}
-
-bool SubmenuView::GetShowSelection(MenuItemView* item) {
- if (drop_item_ == NULL)
- return true;
- // Something is being dropped on one of this menus items. Show the
- // selection if the drop is on the passed in item and the drop position is
- // ON.
- return (drop_item_ == item && drop_position_ == MenuDelegate::DROP_ON);
-}
-
-MenuScrollViewContainer* SubmenuView::GetScrollViewContainer() {
- if (!scroll_view_container_) {
- scroll_view_container_ = new MenuScrollViewContainer(this);
- // Otherwise MenuHost would delete us.
- scroll_view_container_->SetParentOwned(false);
- }
- return scroll_view_container_;
-}
-
-void SubmenuView::PaintDropIndicator(gfx::Canvas* canvas,
- MenuItemView* item,
- MenuDelegate::DropPosition position) {
- if (position == MenuDelegate::DROP_NONE)
- return;
-
- gfx::Rect bounds = CalculateDropIndicatorBounds(item, position);
- canvas->FillRectInt(kDropIndicatorColor, bounds.x(), bounds.y(),
- bounds.width(), bounds.height());
-}
-
-void SubmenuView::SchedulePaintForDropIndicator(
- MenuItemView* item,
- MenuDelegate::DropPosition position) {
- if (item == NULL)
- return;
-
- if (position == MenuDelegate::DROP_ON) {
- item->SchedulePaint();
- } else if (position != MenuDelegate::DROP_NONE) {
- gfx::Rect bounds = CalculateDropIndicatorBounds(item, position);
- SchedulePaint(bounds.x(), bounds.y(), bounds.width(), bounds.height());
- }
-}
-
-gfx::Rect SubmenuView::CalculateDropIndicatorBounds(
- MenuItemView* item,
- MenuDelegate::DropPosition position) {
- DCHECK(position != MenuDelegate::DROP_NONE);
- gfx::Rect item_bounds = item->bounds();
- switch (position) {
- case MenuDelegate::DROP_BEFORE:
- item_bounds.Offset(0, -kDropIndicatorHeight / 2);
- item_bounds.set_height(kDropIndicatorHeight);
- return item_bounds;
-
- case MenuDelegate::DROP_AFTER:
- item_bounds.Offset(0, item_bounds.height() - kDropIndicatorHeight / 2);
- item_bounds.set_height(kDropIndicatorHeight);
- return item_bounds;
-
- default:
- // Don't render anything for on.
- return gfx::Rect();
- }
-}
-
-// MenuItemView ---------------------------------------------------------------
-
-// static
-const int MenuItemView::kMenuItemViewID = 1001;
-
-// static
-bool MenuItemView::allow_task_nesting_during_run_ = false;
-
-MenuItemView::MenuItemView(MenuDelegate* delegate) {
- // NOTE: don't check the delegate for NULL, UpdateMenuPartSizes supplies a
- // NULL delegate.
- Init(NULL, 0, SUBMENU, delegate);
-}
-
-MenuItemView::~MenuItemView() {
- if (controller_) {
- // We're currently showing.
-
- // We can't delete ourselves while we're blocking.
- DCHECK(!controller_->IsBlockingRun());
-
- // Invoking Cancel is going to call us back and notify the delegate.
- // Notifying the delegate from the destructor can be problematic. To avoid
- // this the delegate is set to NULL.
- delegate_ = NULL;
-
- controller_->Cancel(true);
- }
- delete submenu_;
-}
-
-void MenuItemView::RunMenuAt(HWND parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor,
- bool has_mnemonics) {
- PrepareForRun(has_mnemonics);
-
- int mouse_event_flags;
-
- MenuController* controller = MenuController::GetActiveInstance();
- if (controller && !controller->IsBlockingRun()) {
- // A menu is already showing, but it isn't a blocking menu. Cancel it.
- // We can get here during drag and drop if the user right clicks on the
- // menu quickly after the drop.
- controller->Cancel(true);
- controller = NULL;
- }
- bool owns_controller = false;
- if (!controller) {
- // No menus are showing, show one.
- controller = new MenuController(true);
- MenuController::SetActiveInstance(controller);
- owns_controller = true;
- } else {
- // A menu is already showing, use the same controller.
-
- // Don't support blocking from within non-blocking.
- DCHECK(controller->IsBlockingRun());
- }
-
- controller_ = controller;
-
- // Run the loop.
- MenuItemView* result =
- controller->Run(parent, this, bounds, anchor, &mouse_event_flags);
-
- RemoveEmptyMenus();
-
- controller_ = NULL;
-
- if (owns_controller) {
- // We created the controller and need to delete it.
- if (MenuController::GetActiveInstance() == controller)
- MenuController::SetActiveInstance(NULL);
- delete controller;
- }
- // Make sure all the windows we created to show the menus have been
- // destroyed.
- DestroyAllMenuHosts();
- if (result && delegate_)
- delegate_->ExecuteCommand(result->GetCommand(), mouse_event_flags);
-}
-
-void MenuItemView::RunMenuForDropAt(HWND parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor) {
- PrepareForRun(false);
-
- // If there is a menu, hide it so that only one menu is shown during dnd.
- MenuController* current_controller = MenuController::GetActiveInstance();
- if (current_controller) {
- current_controller->Cancel(true);
- }
-
- // Always create a new controller for non-blocking.
- controller_ = new MenuController(false);
-
- // Set the instance, that way it can be canceled by another menu.
- MenuController::SetActiveInstance(controller_);
-
- controller_->Run(parent, this, bounds, anchor, NULL);
-}
-
-void MenuItemView::Cancel() {
- if (controller_ && !canceled_) {
- canceled_ = true;
- controller_->Cancel(true);
- }
-}
-
-SubmenuView* MenuItemView::CreateSubmenu() {
- if (!submenu_)
- submenu_ = new SubmenuView(this);
- return submenu_;
-}
-
-void MenuItemView::SetSelected(bool selected) {
- selected_ = selected;
- SchedulePaint();
-}
-
-void MenuItemView::SetIcon(const SkBitmap& icon, int item_id) {
- MenuItemView* item = GetDescendantByID(item_id);
- DCHECK(item);
- item->SetIcon(icon);
-}
-
-void MenuItemView::SetIcon(const SkBitmap& icon) {
- icon_ = icon;
- SchedulePaint();
-}
-
-void MenuItemView::Paint(gfx::Canvas* canvas) {
- Paint(canvas, false);
-}
-
-gfx::Size MenuItemView::GetPreferredSize() {
- gfx::Font& font = GetRootMenuItem()->font_;
- return gfx::Size(
- font.GetStringWidth(title_) + label_start + item_right_margin,
- font.height() + GetBottomMargin() + GetTopMargin());
-}
-
-MenuController* MenuItemView::GetMenuController() {
- return GetRootMenuItem()->controller_;
-}
-
-MenuDelegate* MenuItemView::GetDelegate() {
- return GetRootMenuItem()->delegate_;
-}
-
-MenuItemView* MenuItemView::GetRootMenuItem() {
- MenuItemView* item = this;
- while (item) {
- MenuItemView* parent = item->GetParentMenuItem();
- if (!parent)
- return item;
- item = parent;
- }
- NOTREACHED();
- return NULL;
-}
-
-wchar_t MenuItemView::GetMnemonic() {
- if (!has_mnemonics_)
- return 0;
-
- const std::wstring& title = GetTitle();
- size_t index = 0;
- do {
- index = title.find('&', index);
- if (index != std::wstring::npos) {
- if (index + 1 != title.size() && title[index + 1] != '&')
- return title[index + 1];
- index++;
- }
- } while (index != std::wstring::npos);
- return 0;
-}
-
-MenuItemView::MenuItemView(MenuItemView* parent,
- int command,
- MenuItemView::Type type) {
- Init(parent, command, type, NULL);
-}
-
-void MenuItemView::Init(MenuItemView* parent,
- int command,
- MenuItemView::Type type,
- MenuDelegate* delegate) {
- delegate_ = delegate;
- controller_ = NULL;
- canceled_ = false;
- parent_menu_item_ = parent;
- type_ = type;
- selected_ = false;
- command_ = command;
- submenu_ = NULL;
- // Assign our ID, this allows SubmenuItemView to find MenuItemViews.
- SetID(kMenuItemViewID);
- has_icons_ = false;
-
- MenuDelegate* root_delegate = GetDelegate();
- if (root_delegate)
- SetEnabled(root_delegate->IsCommandEnabled(command));
-}
-
-MenuItemView* MenuItemView::AppendMenuItemInternal(int item_id,
- const std::wstring& label,
- const SkBitmap& icon,
- Type type) {
- if (!submenu_)
- CreateSubmenu();
- if (type == SEPARATOR) {
- submenu_->AddChildView(new MenuSeparator());
- return NULL;
- }
- MenuItemView* item = new MenuItemView(this, item_id, type);
- if (label.empty() && GetDelegate())
- item->SetTitle(GetDelegate()->GetLabel(item_id));
- else
- item->SetTitle(label);
- item->SetIcon(icon);
- if (type == SUBMENU)
- item->CreateSubmenu();
- submenu_->AddChildView(item);
- return item;
-}
-
-MenuItemView* MenuItemView::GetDescendantByID(int id) {
- if (GetCommand() == id)
- return this;
- if (!HasSubmenu())
- return NULL;
- for (int i = 0; i < GetSubmenu()->GetChildViewCount(); ++i) {
- View* child = GetSubmenu()->GetChildViewAt(i);
- if (child->GetID() == MenuItemView::kMenuItemViewID) {
- MenuItemView* result = static_cast<MenuItemView*>(child)->
- GetDescendantByID(id);
- if (result)
- return result;
- }
- }
- return NULL;
-}
-
-void MenuItemView::DropMenuClosed(bool notify_delegate) {
- DCHECK(controller_);
- DCHECK(!controller_->IsBlockingRun());
- if (MenuController::GetActiveInstance() == controller_)
- MenuController::SetActiveInstance(NULL);
- delete controller_;
- controller_ = NULL;
-
- RemoveEmptyMenus();
-
- if (notify_delegate && delegate_) {
- // Our delegate is null when invoked from the destructor.
- delegate_->DropMenuClosed(this);
- }
- // WARNING: its possible the delegate deleted us at this point.
-}
-
-void MenuItemView::PrepareForRun(bool has_mnemonics) {
- // Currently we only support showing the root.
- DCHECK(!parent_menu_item_);
-
- // Don't invoke run from within run on the same menu.
- DCHECK(!controller_);
-
- // Force us to have a submenu.
- CreateSubmenu();
-
- canceled_ = false;
-
- has_mnemonics_ = has_mnemonics;
-
- AddEmptyMenus();
-
- if (!MenuController::GetActiveInstance()) {
- // Only update the menu size if there are no menus showing, otherwise
- // things may shift around.
- UpdateMenuPartSizes(has_icons_);
- }
-
- font_ = GetMenuFont();
-
- BOOL show_cues;
- show_mnemonics =
- (SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &show_cues, 0) &&
- show_cues == TRUE);
-}
-
-int MenuItemView::GetDrawStringFlags() {
- int flags = 0;
- if (UILayoutIsRightToLeft())
- flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
- else
- flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
-
- if (has_mnemonics_) {
- if (show_mnemonics)
- flags |= gfx::Canvas::SHOW_PREFIX;
- else
- flags |= gfx::Canvas::HIDE_PREFIX;
- }
- return flags;
-}
-
-void MenuItemView::AddEmptyMenus() {
- DCHECK(HasSubmenu());
- if (submenu_->GetChildViewCount() == 0) {
- submenu_->AddChildView(0, new EmptyMenuMenuItem(this));
- } else {
- for (int i = 0, item_count = submenu_->GetMenuItemCount(); i < item_count;
- ++i) {
- MenuItemView* child = submenu_->GetMenuItemAt(i);
- if (child->HasSubmenu())
- child->AddEmptyMenus();
- }
- }
-}
-
-void MenuItemView::RemoveEmptyMenus() {
- DCHECK(HasSubmenu());
- // Iterate backwards as we may end up removing views, which alters the child
- // view count.
- for (int i = submenu_->GetChildViewCount() - 1; i >= 0; --i) {
- View* child = submenu_->GetChildViewAt(i);
- if (child->GetID() == MenuItemView::kMenuItemViewID) {
- MenuItemView* menu_item = static_cast<MenuItemView*>(child);
- if (menu_item->HasSubmenu())
- menu_item->RemoveEmptyMenus();
- } else if (child->GetID() == EmptyMenuMenuItem::kEmptyMenuItemViewID) {
- submenu_->RemoveChildView(child);
- }
- }
-}
-
-void MenuItemView::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
- rect->set_x(MirroredLeftPointForRect(*rect));
-}
-
-void MenuItemView::Paint(gfx::Canvas* canvas, bool for_drag) {
- bool render_selection =
- (!for_drag && IsSelected() &&
- parent_menu_item_->GetSubmenu()->GetShowSelection(this));
- int state = render_selection ? MPI_HOT :
- (IsEnabled() ? MPI_NORMAL : MPI_DISABLED);
- HDC dc = canvas->beginPlatformPaint();
-
- // The gutter is rendered before the background.
- if (render_gutter && !for_drag) {
- gfx::Rect gutter_bounds(label_start - kGutterToLabel - gutter_width, 0,
- gutter_width, height());
- AdjustBoundsForRTLUI(&gutter_bounds);
- RECT gutter_rect = gutter_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER, MPI_NORMAL,
- &gutter_rect);
- }
-
- // Render the background.
- if (!for_drag) {
- gfx::Rect item_bounds(0, 0, width(), height());
- AdjustBoundsForRTLUI(&item_bounds);
- RECT item_rect = item_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuItemBackground(
- NativeTheme::MENU, dc, MENU_POPUPITEM, state, render_selection,
- &item_rect);
- }
-
- int icon_x = kItemLeftMargin;
- int top_margin = GetTopMargin();
- int bottom_margin = GetBottomMargin();
- int icon_y = top_margin + (height() - kItemTopMargin -
- bottom_margin - check_height) / 2;
- int icon_height = check_height;
- int icon_width = check_width;
-
- if (type_ == CHECKBOX && GetDelegate()->IsItemChecked(GetCommand())) {
- // Draw the check background.
- gfx::Rect check_bg_bounds(0, 0, icon_x + icon_width, height());
- const int bg_state = IsEnabled() ? MCB_NORMAL : MCB_DISABLED;
- AdjustBoundsForRTLUI(&check_bg_bounds);
- RECT check_bg_rect = check_bg_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuCheckBackground(
- NativeTheme::MENU, dc, MENU_POPUPCHECKBACKGROUND, bg_state,
- &check_bg_rect);
-
- // And the check.
- gfx::Rect check_bounds(icon_x, icon_y, icon_width, icon_height);
- const int check_state = IsEnabled() ? MC_CHECKMARKNORMAL :
- MC_CHECKMARKDISABLED;
- AdjustBoundsForRTLUI(&check_bounds);
- RECT check_rect = check_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuCheck(
- NativeTheme::MENU, dc, MENU_POPUPCHECK, check_state, &check_rect,
- render_selection);
- }
-
- // Render the foreground.
- // Menu color is specific to Vista, fallback to classic colors if can't
- // get color.
- int default_sys_color = render_selection ? COLOR_HIGHLIGHTTEXT :
- (IsEnabled() ? COLOR_MENUTEXT : COLOR_GRAYTEXT);
- SkColor fg_color = NativeTheme::instance()->GetThemeColorWithDefault(
- NativeTheme::MENU, MENU_POPUPITEM, state, TMT_TEXTCOLOR,
- default_sys_color);
- int width = this->width() - item_right_margin - label_start;
- gfx::Font& font = GetRootMenuItem()->font_;
- gfx::Rect text_bounds(label_start, top_margin, width, font.height());
- text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
- if (for_drag) {
- // With different themes, it's difficult to tell what the correct foreground
- // and background colors are for the text to draw the correct halo. Instead,
- // just draw black on white, which will look good in most cases.
- canvas->DrawStringWithHalo(GetTitle(), font, 0x00000000, 0xFFFFFFFF,
- text_bounds.x(), text_bounds.y(),
- text_bounds.width(), text_bounds.height(),
- GetRootMenuItem()->GetDrawStringFlags());
- } else {
- canvas->DrawStringInt(GetTitle(), font, fg_color,
- text_bounds.x(), text_bounds.y(), text_bounds.width(),
- text_bounds.height(),
- GetRootMenuItem()->GetDrawStringFlags());
- }
-
- if (icon_.width() > 0) {
- gfx::Rect icon_bounds(kItemLeftMargin,
- top_margin + (height() - top_margin -
- bottom_margin - icon_.height()) / 2,
- icon_.width(),
- icon_.height());
- icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
- canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
- }
-
- if (HasSubmenu()) {
- int state_id = IsEnabled() ? MSM_NORMAL : MSM_DISABLED;
- gfx::Rect arrow_bounds(this->width() - item_right_margin + kLabelToArrowPadding,
- 0, arrow_width, height());
- AdjustBoundsForRTLUI(&arrow_bounds);
-
- // If our sub menus open from right to left (which is the case when the
- // locale is RTL) then we should make sure the menu arrow points to the
- // right direction.
- NativeTheme::MenuArrowDirection arrow_direction;
- if (UILayoutIsRightToLeft())
- arrow_direction = NativeTheme::LEFT_POINTING_ARROW;
- else
- arrow_direction = NativeTheme::RIGHT_POINTING_ARROW;
-
- RECT arrow_rect = arrow_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuArrow(
- NativeTheme::MENU, dc, MENU_POPUPSUBMENU, state_id, &arrow_rect,
- arrow_direction, render_selection);
- }
- canvas->endPlatformPaint();
-}
-
-void MenuItemView::DestroyAllMenuHosts() {
- if (!HasSubmenu())
- return;
-
- submenu_->Close();
- for (int i = 0, item_count = submenu_->GetMenuItemCount(); i < item_count;
- ++i) {
- submenu_->GetMenuItemAt(i)->DestroyAllMenuHosts();
- }
-}
-
-int MenuItemView::GetTopMargin() {
- MenuItemView* root = GetRootMenuItem();
- return root && root->has_icons_ ? kItemTopMargin : kItemNoIconTopMargin;
-}
-
-int MenuItemView::GetBottomMargin() {
- MenuItemView* root = GetRootMenuItem();
- return root && root->has_icons_ ?
- kItemBottomMargin : kItemNoIconBottomMargin;
-}
-
-// MenuController ------------------------------------------------------------
-
-// static
-MenuController* MenuController::active_instance_ = NULL;
-
-// static
-MenuController* MenuController::GetActiveInstance() {
- return active_instance_;
-}
-
-#ifdef DEBUG_MENU
-static int instance_count = 0;
-static int nested_depth = 0;
-#endif
-
-MenuItemView* MenuController::Run(HWND parent,
- MenuItemView* root,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition position,
- int* result_mouse_event_flags) {
- exit_all_ = false;
- possible_drag_ = false;
-
- bool nested_menu = showing_;
- if (showing_) {
- // Only support nesting of blocking_run menus, nesting of
- // blocking/non-blocking shouldn't be needed.
- DCHECK(blocking_run_);
-
- // We're already showing, push the current state.
- menu_stack_.push_back(state_);
-
- // The context menu should be owned by the same parent.
- DCHECK(owner_ == parent);
- } else {
- showing_ = true;
- }
-
- // Reset current state.
- pending_state_ = State();
- state_ = State();
- pending_state_.initial_bounds = bounds;
- if (bounds.height() > 1) {
- // Inset the bounds slightly, otherwise drag coordinates don't line up
- // nicely and menus close prematurely.
- pending_state_.initial_bounds.Inset(0, 1);
- }
- pending_state_.anchor = position;
- owner_ = parent;
-
- // Calculate the bounds of the monitor we'll show menus on. Do this once to
- // avoid repeated system queries for the info.
- POINT initial_loc = { bounds.x(), bounds.y() };
- HMONITOR monitor = MonitorFromPoint(initial_loc, MONITOR_DEFAULTTONEAREST);
- if (monitor) {
- MONITORINFO mi = {0};
- mi.cbSize = sizeof(mi);
- GetMonitorInfo(monitor, &mi);
- // Menus appear over the taskbar.
- pending_state_.monitor_bounds = gfx::Rect(mi.rcMonitor);
- }
-
- // Set the selection, which opens the initial menu.
- SetSelection(root, true, true);
-
- if (!blocking_run_) {
- // Start the timer to hide the menu. This is needed as we get no
- // notification when the drag has finished.
- StartCancelAllTimer();
- return NULL;
- }
-
-#ifdef DEBUG_MENU
- nested_depth++;
- DLOG(INFO) << " entering nested loop, depth=" << nested_depth;
-#endif
-
- MessageLoopForUI* loop = MessageLoopForUI::current();
- if (MenuItemView::allow_task_nesting_during_run_) {
- bool did_allow_task_nesting = loop->NestableTasksAllowed();
- loop->SetNestableTasksAllowed(true);
- loop->Run(this);
- loop->SetNestableTasksAllowed(did_allow_task_nesting);
- } else {
- loop->Run(this);
- }
-
-#ifdef DEBUG_MENU
- nested_depth--;
- DLOG(INFO) << " exiting nested loop, depth=" << nested_depth;
-#endif
-
- // Close any open menus.
- SetSelection(NULL, false, true);
-
- if (nested_menu) {
- DCHECK(!menu_stack_.empty());
- // We're running from within a menu, restore the previous state.
- // The menus are already showing, so we don't have to show them.
- state_ = menu_stack_.back();
- pending_state_ = menu_stack_.back();
- menu_stack_.pop_back();
- } else {
- showing_ = false;
- did_capture_ = false;
- }
-
- MenuItemView* result = result_;
- // In case we're nested, reset result_.
- result_ = NULL;
-
- if (result_mouse_event_flags)
- *result_mouse_event_flags = result_mouse_event_flags_;
-
- if (nested_menu && result) {
- // We're nested and about to return a value. The caller might enter another
- // blocking loop. We need to make sure all menus are hidden before that
- // happens otherwise the menus will stay on screen.
- CloseAllNestedMenus();
-
- // Set exit_all_ to true, which makes sure all nested loops exit
- // immediately.
- exit_all_ = true;
- }
-
- return result;
-}
-
-void MenuController::SetSelection(MenuItemView* menu_item,
- bool open_submenu,
- bool update_immediately) {
- size_t paths_differ_at = 0;
- std::vector<MenuItemView*> current_path;
- std::vector<MenuItemView*> new_path;
- BuildPathsAndCalculateDiff(pending_state_.item, menu_item, &current_path,
- &new_path, &paths_differ_at);
-
- size_t current_size = current_path.size();
- size_t new_size = new_path.size();
-
- // Notify the old path it isn't selected.
- for (size_t i = paths_differ_at; i < current_size; ++i)
- current_path[i]->SetSelected(false);
-
- // Notify the new path it is selected.
- for (size_t i = paths_differ_at; i < new_size; ++i)
- new_path[i]->SetSelected(true);
-
- if (menu_item && menu_item->GetDelegate())
- menu_item->GetDelegate()->SelectionChanged(menu_item);
-
- pending_state_.item = menu_item;
- pending_state_.submenu_open = open_submenu;
-
- // Stop timers.
- StopShowTimer();
- StopCancelAllTimer();
-
- if (update_immediately)
- CommitPendingSelection();
- else
- StartShowTimer();
-}
-
-void MenuController::Cancel(bool all) {
- if (!showing_) {
- // This occurs if we're in the process of notifying the delegate for a drop
- // and the delegate cancels us.
- return;
- }
-
- MenuItemView* selected = state_.item;
- exit_all_ = all;
-
- // Hide windows immediately.
- SetSelection(NULL, false, true);
-
- if (!blocking_run_) {
- // If we didn't block the caller we need to notify the menu, which
- // triggers deleting us.
- DCHECK(selected);
- showing_ = false;
- selected->GetRootMenuItem()->DropMenuClosed(true);
- // WARNING: the call to MenuClosed deletes us.
- return;
- }
-}
-
-void MenuController::OnMousePressed(SubmenuView* source,
- const MouseEvent& event) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnMousePressed source=" << source;
-#endif
- if (!blocking_run_)
- return;
-
- MenuPart part =
- GetMenuPartByScreenCoordinate(source, event.x(), event.y());
- if (part.is_scroll())
- return; // Ignore presses on scroll buttons.
-
- if (part.type == MenuPart::NONE ||
- (part.type == MenuPart::MENU_ITEM && part.menu &&
- part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) {
- // Mouse wasn't pressed over any menu, or the active menu, cancel.
-
- // We're going to close and we own the mouse capture. We need to repost the
- // mouse down, otherwise the window the user clicked on won't get the
- // event.
- RepostEvent(source, event);
-
- // And close.
- Cancel(true);
- return;
- }
-
- bool open_submenu = false;
- if (!part.menu) {
- part.menu = part.parent;
- open_submenu = true;
- } else {
- if (part.menu->GetDelegate()->CanDrag(part.menu)) {
- possible_drag_ = true;
- press_x_ = event.x();
- press_y_ = event.y();
- }
- if (part.menu->HasSubmenu())
- open_submenu = true;
- }
- // On a press we immediately commit the selection, that way a submenu
- // pops up immediately rather than after a delay.
- SetSelection(part.menu, open_submenu, true);
-}
-
-void MenuController::OnMouseDragged(SubmenuView* source,
- const MouseEvent& event) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnMouseDragged source=" << source;
-#endif
- MenuPart part =
- GetMenuPartByScreenCoordinate(source, event.x(), event.y());
- UpdateScrolling(part);
-
- if (!blocking_run_)
- return;
-
- if (possible_drag_) {
- if (View::ExceededDragThreshold(event.x() - press_x_,
- event.y() - press_y_)) {
- MenuItemView* item = state_.item;
- DCHECK(item);
- // Points are in the coordinates of the submenu, need to map to that of
- // the selected item. Additionally source may not be the parent of
- // the selected item, so need to map to screen first then to item.
- gfx::Point press_loc(press_x_, press_y_);
- View::ConvertPointToScreen(source->GetScrollViewContainer(), &press_loc);
- View::ConvertPointToView(NULL, item, &press_loc);
- gfx::Point drag_loc(event.location());
- View::ConvertPointToScreen(source->GetScrollViewContainer(), &drag_loc);
- View::ConvertPointToView(NULL, item, &drag_loc);
- gfx::Canvas canvas(item->width(), item->height(), false);
- item->Paint(&canvas, true);
-
- OSExchangeData data;
- item->GetDelegate()->WriteDragData(item, &data);
- drag_utils::SetDragImageOnDataObject(canvas, item->width(),
- item->height(), press_loc.x(),
- press_loc.y(), &data);
-
- scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
- int drag_ops = item->GetDelegate()->GetDragOperations(item);
- DWORD effects;
- StopScrolling();
- DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source,
- DragDropTypes::DragOperationToDropEffect(drag_ops),
- &effects);
- if (GetActiveInstance() == this) {
- if (showing_) {
- // We're still showing, close all menus.
- CloseAllNestedMenus();
- Cancel(true);
- } // else case, drop was on us.
- } // else case, someone canceled us, don't do anything
- }
- return;
- }
- if (part.type == MenuPart::MENU_ITEM) {
- if (!part.menu)
- part.menu = source->GetMenuItem();
- SetSelection(part.menu ? part.menu : state_.item, true, false);
- }
-}
-
-void MenuController::OnMouseReleased(SubmenuView* source,
- const MouseEvent& event) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnMouseReleased source=" << source;
-#endif
- if (!blocking_run_)
- return;
-
- DCHECK(state_.item);
- possible_drag_ = false;
- DCHECK(blocking_run_);
- MenuPart part =
- GetMenuPartByScreenCoordinate(source, event.x(), event.y());
- if (event.IsRightMouseButton() && (part.type == MenuPart::MENU_ITEM &&
- part.menu)) {
- // Set the selection immediately, making sure the submenu is only open
- // if it already was.
- bool open_submenu = (state_.item == pending_state_.item &&
- state_.submenu_open);
- SetSelection(pending_state_.item, open_submenu, true);
- gfx::Point loc(event.location());
- View::ConvertPointToScreen(source->GetScrollViewContainer(), &loc);
-
- // If we open a context menu just return now
- if (part.menu->GetDelegate()->ShowContextMenu(
- part.menu, part.menu->GetCommand(), loc.x(), loc.y(), true))
- return;
- }
-
- if (!part.is_scroll() && part.menu && !part.menu->HasSubmenu()) {
- if (part.menu->GetDelegate()->IsTriggerableEvent(event)) {
- Accept(part.menu, event.GetFlags());
- return;
- }
- } else if (part.type == MenuPart::MENU_ITEM) {
- // User either clicked on empty space, or a menu that has children.
- SetSelection(part.menu ? part.menu : state_.item, true, true);
- }
-}
-
-void MenuController::OnMouseMoved(SubmenuView* source,
- const MouseEvent& event) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnMouseMoved source=" << source;
-#endif
- if (showing_submenu_)
- return;
-
- MenuPart part =
- GetMenuPartByScreenCoordinate(source, event.x(), event.y());
-
- UpdateScrolling(part);
-
- if (!blocking_run_)
- return;
-
- if (part.type == MenuPart::MENU_ITEM && part.menu) {
- SetSelection(part.menu, true, false);
- } else if (!part.is_scroll() && pending_state_.item &&
- (!pending_state_.item->HasSubmenu() ||
- !pending_state_.item->GetSubmenu()->IsShowing())) {
- // On exit if the user hasn't selected an item with a submenu, move the
- // selection back to the parent menu item.
- SetSelection(pending_state_.item->GetParentMenuItem(), true, false);
- }
-}
-
-void MenuController::OnMouseEntered(SubmenuView* source,
- const MouseEvent& event) {
- // MouseEntered is always followed by a mouse moved, so don't need to
- // do anything here.
-}
-
-bool MenuController::CanDrop(SubmenuView* source, const OSExchangeData& data) {
- return source->GetMenuItem()->GetDelegate()->CanDrop(source->GetMenuItem(),
- data);
-}
-
-void MenuController::OnDragEntered(SubmenuView* source,
- const DropTargetEvent& event) {
- valid_drop_coordinates_ = false;
-}
-
-int MenuController::OnDragUpdated(SubmenuView* source,
- const DropTargetEvent& event) {
- StopCancelAllTimer();
-
- gfx::Point screen_loc(event.location());
- View::ConvertPointToScreen(source, &screen_loc);
- if (valid_drop_coordinates_ && screen_loc.x() == drop_x_ &&
- screen_loc.y() == drop_y_) {
- return last_drop_operation_;
- }
- drop_x_ = screen_loc.x();
- drop_y_ = screen_loc.y();
- valid_drop_coordinates_ = true;
-
- MenuItemView* menu_item = GetMenuItemAt(source, event.x(), event.y());
- bool over_empty_menu = false;
- if (!menu_item) {
- // See if we're over an empty menu.
- menu_item = GetEmptyMenuItemAt(source, event.x(), event.y());
- if (menu_item)
- over_empty_menu = true;
- }
- MenuDelegate::DropPosition drop_position = MenuDelegate::DROP_NONE;
- int drop_operation = DragDropTypes::DRAG_NONE;
- if (menu_item) {
- gfx::Point menu_item_loc(event.location());
- View::ConvertPointToView(source, menu_item, &menu_item_loc);
- MenuItemView* query_menu_item;
- if (!over_empty_menu) {
- int menu_item_height = menu_item->height();
- if (menu_item->HasSubmenu() &&
- (menu_item_loc.y() > kDropBetweenPixels &&
- menu_item_loc.y() < (menu_item_height - kDropBetweenPixels))) {
- drop_position = MenuDelegate::DROP_ON;
- } else if (menu_item_loc.y() < menu_item_height / 2) {
- drop_position = MenuDelegate::DROP_BEFORE;
- } else {
- drop_position = MenuDelegate::DROP_AFTER;
- }
- query_menu_item = menu_item;
- } else {
- query_menu_item = menu_item->GetParentMenuItem();
- drop_position = MenuDelegate::DROP_ON;
- }
- drop_operation = menu_item->GetDelegate()->GetDropOperation(
- query_menu_item, event, &drop_position);
-
- if (menu_item->HasSubmenu()) {
- // The menu has a submenu, schedule the submenu to open.
- SetSelection(menu_item, true, false);
- } else {
- SetSelection(menu_item, false, false);
- }
-
- if (drop_position == MenuDelegate::DROP_NONE ||
- drop_operation == DragDropTypes::DRAG_NONE) {
- menu_item = NULL;
- }
- } else {
- SetSelection(source->GetMenuItem(), true, false);
- }
- SetDropMenuItem(menu_item, drop_position);
- last_drop_operation_ = drop_operation;
- return drop_operation;
-}
-
-void MenuController::OnDragExited(SubmenuView* source) {
- StartCancelAllTimer();
-
- if (drop_target_) {
- StopShowTimer();
- SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
- }
-}
-
-int MenuController::OnPerformDrop(SubmenuView* source,
- const DropTargetEvent& event) {
- DCHECK(drop_target_);
- // NOTE: the delegate may delete us after invoking OnPerformDrop, as such
- // we don't call cancel here.
-
- MenuItemView* item = state_.item;
- DCHECK(item);
-
- MenuItemView* drop_target = drop_target_;
- MenuDelegate::DropPosition drop_position = drop_position_;
-
- // Close all menus, including any nested menus.
- SetSelection(NULL, false, true);
- CloseAllNestedMenus();
-
- // Set state such that we exit.
- showing_ = false;
- exit_all_ = true;
-
- if (!IsBlockingRun())
- item->GetRootMenuItem()->DropMenuClosed(false);
-
- // WARNING: the call to MenuClosed deletes us.
-
- // If over an empty menu item, drop occurs on the parent.
- if (drop_target->GetID() == EmptyMenuMenuItem::kEmptyMenuItemViewID)
- drop_target = drop_target->GetParentMenuItem();
-
- return drop_target->GetDelegate()->OnPerformDrop(
- drop_target, drop_position, event);
-}
-
-void MenuController::OnDragEnteredScrollButton(SubmenuView* source,
- bool is_up) {
- MenuPart part;
- part.type = is_up ? MenuPart::SCROLL_UP : MenuPart::SCROLL_DOWN;
- part.submenu = source;
- UpdateScrolling(part);
-
- // Do this to force the selection to hide.
- SetDropMenuItem(source->GetMenuItemAt(0), MenuDelegate::DROP_NONE);
-
- StopCancelAllTimer();
-}
-
-void MenuController::OnDragExitedScrollButton(SubmenuView* source) {
- StartCancelAllTimer();
- SetDropMenuItem(NULL, MenuDelegate::DROP_NONE);
- StopScrolling();
-}
-
-// static
-void MenuController::SetActiveInstance(MenuController* controller) {
- active_instance_ = controller;
-}
-
-bool MenuController::Dispatch(const MSG& msg) {
- DCHECK(blocking_run_);
-
- if (exit_all_) {
- // We must translate/dispatch the message here, otherwise we would drop
- // the message on the floor.
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- return false;
- }
-
- // NOTE: we don't get WM_ACTIVATE or anything else interesting in here.
- switch (msg.message) {
- case WM_CONTEXTMENU: {
- MenuItemView* item = pending_state_.item;
- if (item && item->GetRootMenuItem() != item) {
- gfx::Point screen_loc(0, item->height());
- View::ConvertPointToScreen(item, &screen_loc);
- item->GetDelegate()->ShowContextMenu(
- item, item->GetCommand(), screen_loc.x(), screen_loc.y(), false);
- }
- return true;
- }
-
- // NOTE: focus wasn't changed when the menu was shown. As such, don't
- // dispatch key events otherwise the focused window will get the events.
- case WM_KEYDOWN:
- return OnKeyDown(msg);
-
- case WM_CHAR:
- return OnChar(msg);
-
- case WM_KEYUP:
- return true;
-
- case WM_SYSKEYUP:
- // We may have been shown on a system key, as such don't do anything
- // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and
- // close the menu.
- return true;
-
- case WM_CANCELMODE:
- case WM_SYSKEYDOWN:
- // Exit immediately on system keys.
- Cancel(true);
- return false;
-
- default:
- break;
- }
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- return !exit_all_;
-}
-
-bool MenuController::OnKeyDown(const MSG& msg) {
- DCHECK(blocking_run_);
-
- switch (msg.wParam) {
- case VK_UP:
- IncrementSelection(-1);
- break;
-
- case VK_DOWN:
- IncrementSelection(1);
- break;
-
- // Handling of VK_RIGHT and VK_LEFT is different depending on the UI
- // layout.
- case VK_RIGHT:
- if (l10n_util::TextDirection() == l10n_util::RIGHT_TO_LEFT)
- CloseSubmenu();
- else
- OpenSubmenuChangeSelectionIfCan();
- break;
-
- case VK_LEFT:
- if (l10n_util::TextDirection() == l10n_util::RIGHT_TO_LEFT)
- OpenSubmenuChangeSelectionIfCan();
- else
- CloseSubmenu();
- break;
-
- case VK_RETURN:
- if (pending_state_.item) {
- if (pending_state_.item->HasSubmenu()) {
- OpenSubmenuChangeSelectionIfCan();
- } else if (pending_state_.item->IsEnabled()) {
- Accept(pending_state_.item, 0);
- return false;
- }
- }
- break;
-
- case VK_ESCAPE:
- if (!state_.item->GetParentMenuItem() ||
- (!state_.item->GetParentMenuItem()->GetParentMenuItem() &&
- (!state_.item->HasSubmenu() ||
- !state_.item->GetSubmenu()->IsShowing()))) {
- // User pressed escape and only one menu is shown, cancel it.
- Cancel(false);
- return false;
- } else {
- CloseSubmenu();
- }
- break;
-
- case VK_APPS:
- break;
-
- default:
- TranslateMessage(&msg);
- break;
- }
- return true;
-}
-
-bool MenuController::OnChar(const MSG& msg) {
- DCHECK(blocking_run_);
-
- return !SelectByChar(static_cast<wchar_t>(msg.wParam));
-}
-
-MenuController::MenuController(bool blocking)
- : blocking_run_(blocking),
- showing_(false),
- exit_all_(false),
- did_capture_(false),
- result_(NULL),
- drop_target_(NULL),
- owner_(NULL),
- possible_drag_(false),
- valid_drop_coordinates_(false),
- showing_submenu_(false),
- result_mouse_event_flags_(0) {
-#ifdef DEBUG_MENU
- instance_count++;
- DLOG(INFO) << "created MC, count=" << instance_count;
-#endif
-}
-
-MenuController::~MenuController() {
- DCHECK(!showing_);
- StopShowTimer();
- StopCancelAllTimer();
-#ifdef DEBUG_MENU
- instance_count--;
- DLOG(INFO) << "destroyed MC, count=" << instance_count;
-#endif
-}
-
-void MenuController::Accept(MenuItemView* item, int mouse_event_flags) {
- DCHECK(IsBlockingRun());
- result_ = item;
- exit_all_ = true;
- result_mouse_event_flags_ = mouse_event_flags;
-}
-
-void MenuController::CloseAllNestedMenus() {
- for (std::list<State>::iterator i = menu_stack_.begin();
- i != menu_stack_.end(); ++i) {
- MenuItemView* item = i->item;
- MenuItemView* last_item = item;
- while (item) {
- CloseMenu(item);
- last_item = item;
- item = item->GetParentMenuItem();
- }
- i->submenu_open = false;
- i->item = last_item;
- }
-}
-
-MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) {
- View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y));
- if (child_under_mouse && child_under_mouse->IsEnabled() &&
- child_under_mouse->GetID() == MenuItemView::kMenuItemViewID) {
- return static_cast<MenuItemView*>(child_under_mouse);
- }
- return NULL;
-}
-
-MenuItemView* MenuController::GetEmptyMenuItemAt(View* source, int x, int y) {
- View* child_under_mouse = source->GetViewForPoint(gfx::Point(x, y));
- if (child_under_mouse &&
- child_under_mouse->GetID() == EmptyMenuMenuItem::kEmptyMenuItemViewID) {
- return static_cast<MenuItemView*>(child_under_mouse);
- }
- return NULL;
-}
-
-bool MenuController::IsScrollButtonAt(SubmenuView* source,
- int x,
- int y,
- MenuPart::Type* part) {
- MenuScrollViewContainer* scroll_view = source->GetScrollViewContainer();
- View* child_under_mouse = scroll_view->GetViewForPoint(gfx::Point(x, y));
- if (child_under_mouse && child_under_mouse->IsEnabled()) {
- if (child_under_mouse == scroll_view->scroll_up_button()) {
- *part = MenuPart::SCROLL_UP;
- return true;
- }
- if (child_under_mouse == scroll_view->scroll_down_button()) {
- *part = MenuPart::SCROLL_DOWN;
- return true;
- }
- }
- return false;
-}
-
-MenuController::MenuPart MenuController::GetMenuPartByScreenCoordinate(
- SubmenuView* source,
- int source_x,
- int source_y) {
- MenuPart part;
-
- gfx::Point screen_loc(source_x, source_y);
- View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
-
- MenuItemView* item = state_.item;
- while (item) {
- if (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
- GetMenuPartByScreenCoordinateImpl(item->GetSubmenu(), screen_loc,
- &part)) {
- return part;
- }
- item = item->GetParentMenuItem();
- }
-
- return part;
-}
-
-bool MenuController::GetMenuPartByScreenCoordinateImpl(
- SubmenuView* menu,
- const gfx::Point& screen_loc,
- MenuPart* part) {
- // Is the mouse over the scroll buttons?
- gfx::Point scroll_view_loc = screen_loc;
- View* scroll_view_container = menu->GetScrollViewContainer();
- View::ConvertPointToView(NULL, scroll_view_container, &scroll_view_loc);
- if (scroll_view_loc.x() < 0 ||
- scroll_view_loc.x() >= scroll_view_container->width() ||
- scroll_view_loc.y() < 0 ||
- scroll_view_loc.y() >= scroll_view_container->height()) {
- // Point isn't contained in menu.
- return false;
- }
- if (IsScrollButtonAt(menu, scroll_view_loc.x(), scroll_view_loc.y(),
- &(part->type))) {
- part->submenu = menu;
- return true;
- }
-
- // Not over the scroll button. Check the actual menu.
- if (DoesSubmenuContainLocation(menu, screen_loc)) {
- gfx::Point menu_loc = screen_loc;
- View::ConvertPointToView(NULL, menu, &menu_loc);
- part->menu = GetMenuItemAt(menu, menu_loc.x(), menu_loc.y());
- part->type = MenuPart::MENU_ITEM;
- if (!part->menu)
- part->parent = menu->GetMenuItem();
- return true;
- }
-
- // While the mouse isn't over a menu item or the scroll buttons of menu, it
- // is contained by menu and so we return true. If we didn't return true other
- // menus would be searched, even though they are likely obscured by us.
- return true;
-}
-
-bool MenuController::DoesSubmenuContainLocation(SubmenuView* submenu,
- const gfx::Point& screen_loc) {
- gfx::Point view_loc = screen_loc;
- View::ConvertPointToView(NULL, submenu, &view_loc);
- gfx::Rect vis_rect = submenu->GetVisibleBounds();
- return vis_rect.Contains(view_loc.x(), view_loc.y());
-}
-
-void MenuController::CommitPendingSelection() {
- StopShowTimer();
-
- size_t paths_differ_at = 0;
- std::vector<MenuItemView*> current_path;
- std::vector<MenuItemView*> new_path;
- BuildPathsAndCalculateDiff(state_.item, pending_state_.item, &current_path,
- &new_path, &paths_differ_at);
-
- // Hide the old menu.
- for (size_t i = paths_differ_at; i < current_path.size(); ++i) {
- if (current_path[i]->HasSubmenu()) {
- current_path[i]->GetSubmenu()->Hide();
- }
- }
-
- // Copy pending to state_, making sure to preserve the direction menus were
- // opened.
- std::list<bool> pending_open_direction;
- state_.open_leading.swap(pending_open_direction);
- state_ = pending_state_;
- state_.open_leading.swap(pending_open_direction);
-
- int menu_depth = MenuDepth(state_.item);
- if (menu_depth == 0) {
- state_.open_leading.clear();
- } else {
- int cached_size = static_cast<int>(state_.open_leading.size());
- DCHECK(menu_depth >= 0);
- while (cached_size-- >= menu_depth)
- state_.open_leading.pop_back();
- }
-
- if (!state_.item) {
- // Nothing to select.
- StopScrolling();
- return;
- }
-
- // Open all the submenus preceeding the last menu item (last menu item is
- // handled next).
- if (new_path.size() > 1) {
- for (std::vector<MenuItemView*>::iterator i = new_path.begin();
- i != new_path.end() - 1; ++i) {
- OpenMenu(*i);
- }
- }
-
- if (state_.submenu_open) {
- // The submenu should be open, open the submenu if the item has a submenu.
- if (state_.item->HasSubmenu()) {
- OpenMenu(state_.item);
- } else {
- state_.submenu_open = false;
- }
- } else if (state_.item->HasSubmenu() &&
- state_.item->GetSubmenu()->IsShowing()) {
- state_.item->GetSubmenu()->Hide();
- }
-
- if (scroll_task_.get() && scroll_task_->submenu()) {
- // Stop the scrolling if none of the elements of the selection contain
- // the menu being scrolled.
- bool found = false;
- MenuItemView* item = state_.item;
- while (item && !found) {
- found = (item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
- item->GetSubmenu() == scroll_task_->submenu());
- item = item->GetParentMenuItem();
- }
- if (!found)
- StopScrolling();
- }
-}
-
-void MenuController::CloseMenu(MenuItemView* item) {
- DCHECK(item);
- if (!item->HasSubmenu())
- return;
- item->GetSubmenu()->Hide();
-}
-
-void MenuController::OpenMenu(MenuItemView* item) {
- DCHECK(item);
- if (item->GetSubmenu()->IsShowing()) {
- return;
- }
-
- bool prefer_leading =
- state_.open_leading.empty() ? true : state_.open_leading.back();
- bool resulting_direction;
- gfx::Rect bounds =
- CalculateMenuBounds(item, prefer_leading, &resulting_direction);
- state_.open_leading.push_back(resulting_direction);
- bool do_capture = (!did_capture_ && blocking_run_);
- showing_submenu_ = true;
- item->GetSubmenu()->ShowAt(owner_, bounds, do_capture);
- showing_submenu_ = false;
- did_capture_ = true;
-}
-
-void MenuController::BuildPathsAndCalculateDiff(
- MenuItemView* old_item,
- MenuItemView* new_item,
- std::vector<MenuItemView*>* old_path,
- std::vector<MenuItemView*>* new_path,
- size_t* first_diff_at) {
- DCHECK(old_path && new_path && first_diff_at);
- BuildMenuItemPath(old_item, old_path);
- BuildMenuItemPath(new_item, new_path);
-
- size_t common_size = std::min(old_path->size(), new_path->size());
-
- // Find the first difference between the two paths, when the loop
- // returns, diff_i is the first index where the two paths differ.
- for (size_t i = 0; i < common_size; ++i) {
- if ((*old_path)[i] != (*new_path)[i]) {
- *first_diff_at = i;
- return;
- }
- }
-
- *first_diff_at = common_size;
-}
-
-void MenuController::BuildMenuItemPath(MenuItemView* item,
- std::vector<MenuItemView*>* path) {
- if (!item)
- return;
- BuildMenuItemPath(item->GetParentMenuItem(), path);
- path->push_back(item);
-}
-
-void MenuController::StartShowTimer() {
- show_timer_.Start(TimeDelta::FromMilliseconds(kShowDelay), this,
- &MenuController::CommitPendingSelection);
-}
-
-void MenuController::StopShowTimer() {
- show_timer_.Stop();
-}
-
-void MenuController::StartCancelAllTimer() {
- cancel_all_timer_.Start(TimeDelta::FromMilliseconds(kCloseOnExitTime),
- this, &MenuController::CancelAll);
-}
-
-void MenuController::StopCancelAllTimer() {
- cancel_all_timer_.Stop();
-}
-
-gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item,
- bool prefer_leading,
- bool* is_leading) {
- DCHECK(item);
-
- SubmenuView* submenu = item->GetSubmenu();
- DCHECK(submenu);
-
- gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize();
-
- // Don't let the menu go to wide. This is some where between what IE and FF
- // do.
- pref.set_width(std::min(pref.width(), kMaxMenuWidth));
- if (!state_.monitor_bounds.IsEmpty())
- pref.set_width(std::min(pref.width(), state_.monitor_bounds.width()));
-
- // Assume we can honor prefer_leading.
- *is_leading = prefer_leading;
-
- int x, y;
-
- if (!item->GetParentMenuItem()) {
- // First item, position relative to initial location.
- x = state_.initial_bounds.x();
- y = state_.initial_bounds.bottom();
- if (state_.anchor == MenuItemView::TOPRIGHT)
- x = x + state_.initial_bounds.width() - pref.width();
- if (!state_.monitor_bounds.IsEmpty() &&
- y + pref.height() > state_.monitor_bounds.bottom()) {
- // The menu doesn't fit on screen. If the first location is above the
- // half way point, show from the mouse location to bottom of screen.
- // Otherwise show from the top of the screen to the location of the mouse.
- // While odd, this behavior matches IE.
- if (y < (state_.monitor_bounds.y() +
- state_.monitor_bounds.height() / 2)) {
- pref.set_height(std::min(pref.height(),
- state_.monitor_bounds.bottom() - y));
- } else {
- pref.set_height(std::min(pref.height(),
- state_.initial_bounds.y() - state_.monitor_bounds.y()));
- y = state_.initial_bounds.y() - pref.height();
- }
- }
- } else {
- // Not the first menu; position it relative to the bounds of the menu
- // item.
- gfx::Point item_loc;
- View::ConvertPointToScreen(item, &item_loc);
-
- // We must make sure we take into account the UI layout. If the layout is
- // RTL, then a 'leading' menu is positioned to the left of the parent menu
- // item and not to the right.
- bool layout_is_rtl = item->UILayoutIsRightToLeft();
- bool create_on_the_right = (prefer_leading && !layout_is_rtl) ||
- (!prefer_leading && layout_is_rtl);
-
- if (create_on_the_right) {
- x = item_loc.x() + item->width() - kSubmenuHorizontalInset;
- if (state_.monitor_bounds.width() != 0 &&
- x + pref.width() > state_.monitor_bounds.right()) {
- if (layout_is_rtl)
- *is_leading = true;
- else
- *is_leading = false;
- x = item_loc.x() - pref.width() + kSubmenuHorizontalInset;
- }
- } else {
- x = item_loc.x() - pref.width() + kSubmenuHorizontalInset;
- if (state_.monitor_bounds.width() != 0 && x < state_.monitor_bounds.x()) {
- if (layout_is_rtl)
- *is_leading = false;
- else
- *is_leading = true;
- x = item_loc.x() + item->width() - kSubmenuHorizontalInset;
- }
- }
- y = item_loc.y() - kSubmenuBorderSize;
- if (state_.monitor_bounds.width() != 0) {
- pref.set_height(std::min(pref.height(), state_.monitor_bounds.height()));
- if (y + pref.height() > state_.monitor_bounds.bottom())
- y = state_.monitor_bounds.bottom() - pref.height();
- if (y < state_.monitor_bounds.y())
- y = state_.monitor_bounds.y();
- }
- }
-
- if (state_.monitor_bounds.width() != 0) {
- if (x + pref.width() > state_.monitor_bounds.right())
- x = state_.monitor_bounds.right() - pref.width();
- if (x < state_.monitor_bounds.x())
- x = state_.monitor_bounds.x();
- }
- return gfx::Rect(x, y, pref.width(), pref.height());
-}
-
-// static
-int MenuController::MenuDepth(MenuItemView* item) {
- if (!item)
- return 0;
- return MenuDepth(item->GetParentMenuItem()) + 1;
-}
-
-void MenuController::IncrementSelection(int delta) {
- MenuItemView* item = pending_state_.item;
- DCHECK(item);
- if (pending_state_.submenu_open && item->HasSubmenu() &&
- item->GetSubmenu()->IsShowing()) {
- // A menu is selected and open, but none of its children are selected,
- // select the first menu item.
- if (item->GetSubmenu()->GetMenuItemCount()) {
- SetSelection(item->GetSubmenu()->GetMenuItemAt(0), false, false);
- ScrollToVisible(item->GetSubmenu()->GetMenuItemAt(0));
- return; // return so else case can fall through.
- }
- }
- if (item->GetParentMenuItem()) {
- MenuItemView* parent = item->GetParentMenuItem();
- int parent_count = parent->GetSubmenu()->GetMenuItemCount();
- if (parent_count > 1) {
- for (int i = 0; i < parent_count; ++i) {
- if (parent->GetSubmenu()->GetMenuItemAt(i) == item) {
- int next_index = (i + delta + parent_count) % parent_count;
- ScrollToVisible(parent->GetSubmenu()->GetMenuItemAt(next_index));
- SetSelection(parent->GetSubmenu()->GetMenuItemAt(next_index), false,
- false);
- break;
- }
- }
- }
- }
-}
-
-void MenuController::OpenSubmenuChangeSelectionIfCan() {
- MenuItemView* item = pending_state_.item;
- if (item->HasSubmenu()) {
- if (item->GetSubmenu()->GetMenuItemCount() > 0) {
- SetSelection(item->GetSubmenu()->GetMenuItemAt(0), false, true);
- } else {
- // No menu items, just show the sub-menu.
- SetSelection(item, true, true);
- }
- }
-}
-
-void MenuController::CloseSubmenu() {
- MenuItemView* item = state_.item;
- DCHECK(item);
- if (!item->GetParentMenuItem())
- return;
- if (item->HasSubmenu() && item->GetSubmenu()->IsShowing()) {
- SetSelection(item, false, true);
- } else if (item->GetParentMenuItem()->GetParentMenuItem()) {
- SetSelection(item->GetParentMenuItem(), false, true);
- }
-}
-
-bool MenuController::IsMenuWindow(MenuItemView* item, HWND window) {
- if (!item)
- return false;
- return ((item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
- item->GetSubmenu()->GetWidget()->GetNativeView() == window) ||
- IsMenuWindow(item->GetParentMenuItem(), window));
-}
-
-bool MenuController::SelectByChar(wchar_t character) {
- wchar_t char_array[1] = { character };
- wchar_t key = l10n_util::ToLower(char_array)[0];
- MenuItemView* item = pending_state_.item;
- if (!item->HasSubmenu() || !item->GetSubmenu()->IsShowing())
- item = item->GetParentMenuItem();
- DCHECK(item);
- DCHECK(item->HasSubmenu());
- SubmenuView* submenu = item->GetSubmenu();
- DCHECK(submenu);
- int menu_item_count = submenu->GetMenuItemCount();
- if (!menu_item_count)
- return false;
- for (int i = 0; i < menu_item_count; ++i) {
- MenuItemView* child = submenu->GetMenuItemAt(i);
- if (child->GetMnemonic() == key && child->IsEnabled()) {
- Accept(child, 0);
- return true;
- }
- }
-
- // No matching mnemonic, search through items that don't have mnemonic
- // based on first character of the title.
- int first_match = -1;
- bool has_multiple = false;
- int next_match = -1;
- int index_of_item = -1;
- for (int i = 0; i < menu_item_count; ++i) {
- MenuItemView* child = submenu->GetMenuItemAt(i);
- if (!child->GetMnemonic() && child->IsEnabled()) {
- std::wstring lower_title = l10n_util::ToLower(child->GetTitle());
- if (child == pending_state_.item)
- index_of_item = i;
- if (lower_title.length() && lower_title[0] == key) {
- if (first_match == -1)
- first_match = i;
- else
- has_multiple = true;
- if (next_match == -1 && index_of_item != -1 && i > index_of_item)
- next_match = i;
- }
- }
- }
- if (first_match != -1) {
- if (!has_multiple) {
- if (submenu->GetMenuItemAt(first_match)->HasSubmenu()) {
- SetSelection(submenu->GetMenuItemAt(first_match), true, false);
- } else {
- Accept(submenu->GetMenuItemAt(first_match), 0);
- return true;
- }
- } else if (index_of_item == -1 || next_match == -1) {
- SetSelection(submenu->GetMenuItemAt(first_match), false, false);
- } else {
- SetSelection(submenu->GetMenuItemAt(next_match), false, false);
- }
- }
- return false;
-}
-
-void MenuController::RepostEvent(SubmenuView* source,
- const MouseEvent& event) {
- gfx::Point screen_loc(event.location());
- View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
- HWND window = WindowFromPoint(screen_loc.ToPOINT());
- if (window) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "RepostEvent on press";
-#endif
-
- // Release the capture.
- SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu();
- submenu->ReleaseCapture();
-
- if (submenu->host() && submenu->host()->GetNativeView() &&
- GetWindowThreadProcessId(submenu->host()->GetNativeView(), NULL) !=
- GetWindowThreadProcessId(window, NULL)) {
- // Even though we have mouse capture, windows generates a mouse event
- // if the other window is in a separate thread. Don't generate an event in
- // this case else the target window can get double events leading to bad
- // behavior.
- return;
- }
-
- // Convert the coordinates to the target window.
- RECT window_bounds;
- GetWindowRect(window, &window_bounds);
- int window_x = screen_loc.x() - window_bounds.left;
- int window_y = screen_loc.y() - window_bounds.top;
-
- // Determine whether the click was in the client area or not.
- // NOTE: WM_NCHITTEST coordinates are relative to the screen.
- LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0,
- MAKELPARAM(screen_loc.x(),
- screen_loc.y()));
- const bool in_client_area = (nc_hit_result == HTCLIENT);
-
- // TODO(sky): this isn't right. The event to generate should correspond
- // with the event we just got. MouseEvent only tells us what is down,
- // which may differ. Need to add ability to get changed button from
- // MouseEvent.
- int event_type;
- if (event.IsLeftMouseButton())
- event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
- else if (event.IsMiddleMouseButton())
- event_type = in_client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN;
- else if (event.IsRightMouseButton())
- event_type = in_client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN;
- else
- event_type = 0; // Unknown mouse press.
-
- if (event_type) {
- if (in_client_area) {
- PostMessage(window, event_type, event.GetWindowsFlags(),
- MAKELPARAM(window_x, window_y));
- } else {
- PostMessage(window, event_type, nc_hit_result,
- MAKELPARAM(screen_loc.x(), screen_loc.y()));
- }
- }
- }
-}
-
-void MenuController::SetDropMenuItem(
- MenuItemView* new_target,
- MenuDelegate::DropPosition new_position) {
- if (new_target == drop_target_ && new_position == drop_position_)
- return;
-
- if (drop_target_) {
- drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
- NULL, MenuDelegate::DROP_NONE);
- }
- drop_target_ = new_target;
- drop_position_ = new_position;
- if (drop_target_) {
- drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem(
- drop_target_, drop_position_);
- }
-}
-
-void MenuController::UpdateScrolling(const MenuPart& part) {
- if (!part.is_scroll() && !scroll_task_.get())
- return;
-
- if (!scroll_task_.get())
- scroll_task_.reset(new MenuScrollTask());
- scroll_task_->Update(part);
-}
-
-void MenuController::StopScrolling() {
- scroll_task_.reset(NULL);
-}
-
-} // namespace views
diff --git a/views/controls/menu/chrome_menu.h b/views/controls/menu/chrome_menu.h
deleted file mode 100644
index 89d16d41..0000000
--- a/views/controls/menu/chrome_menu.h
+++ /dev/null
@@ -1,962 +0,0 @@
-// 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 VIEWS_CONTROLS_MENU_CHROME_MENU_H_
-#define VIEWS_CONTROLS_MENU_CHROME_MENU_H_
-
-#include <list>
-#include <vector>
-
-#include "app/drag_drop_types.h"
-#include "app/gfx/font.h"
-#include "base/gfx/native_widget_types.h"
-#include "base/gfx/point.h"
-#include "base/gfx/rect.h"
-#include "base/message_loop.h"
-#include "base/task.h"
-#include "base/timer.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "views/controls/menu/controller.h"
-#include "views/event.h"
-#include "views/view.h"
-
-namespace views {
-
-class MenuController;
-class MenuHost;
-class MenuItemView;
-class MenuScrollViewContainer;
-class SubmenuView;
-
-namespace {
-class MenuHostRootView;
-}
-
-// MenuDelegate --------------------------------------------------------------
-
-// Delegate for the menu.
-
-class MenuDelegate : Controller {
- public:
- // Used during drag and drop to indicate where the drop indicator should
- // be rendered.
- enum DropPosition {
- // Indicates a drop is not allowed here.
- DROP_NONE,
-
- // Indicates the drop should occur before the item.
- DROP_BEFORE,
-
- // Indicates the drop should occur after the item.
- DROP_AFTER,
-
- // Indicates the drop should occur on the item.
- DROP_ON
- };
-
- // Whether or not an item should be shown as checked.
- // TODO(sky): need checked support.
- virtual bool IsItemChecked(int id) const {
- return false;
- }
-
- // The string shown for the menu item. This is only invoked when an item is
- // added with an empty label.
- virtual std::wstring GetLabel(int id) const {
- return std::wstring();
- }
-
- // Shows the context menu with the specified id. This is invoked when the
- // user does the appropriate gesture to show a context menu. The id
- // identifies the id of the menu to show the context menu for.
- // is_mouse_gesture is true if this is the result of a mouse gesture.
- // If this is not the result of a mouse gesture x/y is the recommended
- // location to display the content menu at. In either case, x/y is in
- // screen coordinates.
- // Returns true if a context menu was displayed, otherwise false
- virtual bool ShowContextMenu(MenuItemView* source,
- int id,
- int x,
- int y,
- bool is_mouse_gesture) {
- return false;
- }
-
- // Controller
- virtual bool SupportsCommand(int id) const {
- return true;
- }
- virtual bool IsCommandEnabled(int id) const {
- return true;
- }
- virtual bool GetContextualLabel(int id, std::wstring* out) const {
- return false;
- }
- virtual void ExecuteCommand(int id) {
- }
-
- // Executes the specified command. mouse_event_flags give the flags of the
- // mouse event that triggered this to be invoked (views::MouseEvent
- // flags). mouse_event_flags is 0 if this is triggered by a user gesture
- // other than a mouse event.
- virtual void ExecuteCommand(int id, int mouse_event_flags) {
- ExecuteCommand(id);
- }
-
- // Returns true if the specified mouse event is one the user can use
- // to trigger, or accept, the mouse. Defaults to left or right mouse buttons.
- virtual bool IsTriggerableEvent(const MouseEvent& e) {
- return e.IsLeftMouseButton() || e.IsRightMouseButton();
- }
-
- // Invoked to determine if drops can be accepted for a submenu. This is
- // ONLY invoked for menus that have submenus and indicates whether or not
- // a drop can occur on any of the child items of the item. For example,
- // consider the following menu structure:
- //
- // A
- // B
- // C
- //
- // Where A has a submenu with children B and C. This is ONLY invoked for
- // A, not B and C.
- //
- // To restrict which children can be dropped on override GetDropOperation.
- virtual bool CanDrop(MenuItemView* menu, const OSExchangeData& data) {
- return false;
- }
-
- // Returns the drop operation for the specified target menu item. This is
- // only invoked if CanDrop returned true for the parent menu. position
- // is set based on the location of the mouse, reset to specify a different
- // position.
- //
- // If a drop should not be allowed, returned DragDropTypes::DRAG_NONE.
- virtual int GetDropOperation(MenuItemView* item,
- const DropTargetEvent& event,
- DropPosition* position) {
- NOTREACHED() << "If you override CanDrop, you need to override this too";
- return DragDropTypes::DRAG_NONE;
- }
-
- // Invoked to perform the drop operation. This is ONLY invoked if
- // canDrop returned true for the parent menu item, and GetDropOperation
- // returned an operation other than DragDropTypes::DRAG_NONE.
- //
- // menu indicates the menu the drop occurred on.
- virtual int OnPerformDrop(MenuItemView* menu,
- DropPosition position,
- const DropTargetEvent& event) {
- NOTREACHED() << "If you override CanDrop, you need to override this too";
- return DragDropTypes::DRAG_NONE;
- }
-
- // Invoked to determine if it is possible for the user to drag the specified
- // menu item.
- virtual bool CanDrag(MenuItemView* menu) {
- return false;
- }
-
- // Invoked to write the data for a drag operation to data. sender is the
- // MenuItemView being dragged.
- virtual void WriteDragData(MenuItemView* sender, OSExchangeData* data) {
- NOTREACHED() << "If you override CanDrag, you must override this too.";
- }
-
- // Invoked to determine the drag operations for a drag session of sender.
- // See DragDropTypes for possible values.
- virtual int GetDragOperations(MenuItemView* sender) {
- NOTREACHED() << "If you override CanDrag, you must override this too.";
- return 0;
- }
-
- // Notification the menu has closed. This is only sent when running the
- // menu for a drop.
- virtual void DropMenuClosed(MenuItemView* menu) {
- }
-
- // Notification that the user has highlighted the specified item.
- virtual void SelectionChanged(MenuItemView* menu) {
- }
-};
-
-// MenuItemView --------------------------------------------------------------
-
-// MenuItemView represents a single menu item with a label and optional icon.
-// Each MenuItemView may also contain a submenu, which in turn may contain
-// any number of child MenuItemViews.
-//
-// To use a menu create an initial MenuItemView using the constructor that
-// takes a MenuDelegate, then create any number of child menu items by way
-// of the various AddXXX methods.
-//
-// MenuItemView is itself a View, which means you can add Views to each
-// MenuItemView. This normally NOT want you want, rather add other child Views
-// to the submenu of the MenuItemView.
-//
-// There are two ways to show a MenuItemView:
-// 1. Use RunMenuAt. This blocks the caller, executing the selected command
-// on success.
-// 2. Use RunMenuForDropAt. This is intended for use during a drop session
-// and does NOT block the caller. Instead the delegate is notified when the
-// menu closes via the DropMenuClosed method.
-
-class MenuItemView : public View {
- friend class MenuController;
-
- public:
- // ID used to identify menu items.
- static const int kMenuItemViewID;
-
- // If true SetNestableTasksAllowed(true) is invoked before MessageLoop::Run
- // is invoked. This is only useful for testing and defaults to false.
- static bool allow_task_nesting_during_run_;
-
- // Different types of menu items.
- enum Type {
- NORMAL,
- SUBMENU,
- CHECKBOX,
- RADIO,
- SEPARATOR
- };
-
- // Where the menu should be anchored to.
- enum AnchorPosition {
- TOPLEFT,
- TOPRIGHT
- };
-
- // Constructor for use with the top level menu item. This menu is never
- // shown to the user, rather its use as the parent for all menu items.
- explicit MenuItemView(MenuDelegate* delegate);
-
- virtual ~MenuItemView();
-
- // Run methods. See description above class for details. Both Run methods take
- // a rectangle, which is used to position the menu. |has_mnemonics| indicates
- // whether the items have mnemonics. Mnemonics are identified by way of the
- // character following the '&'.
- void RunMenuAt(gfx::NativeView parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor,
- bool has_mnemonics);
- void RunMenuForDropAt(gfx::NativeView parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor);
-
- // Hides and cancels the menu. This does nothing if the menu is not open.
- void Cancel();
-
- // Adds an item to this menu.
- // item_id The id of the item, used to identify it in delegate callbacks
- // or (if delegate is NULL) to identify the command associated
- // with this item with the controller specified in the ctor. Note
- // that this value should not be 0 as this has a special meaning
- // ("NULL command, no item selected")
- // label The text label shown.
- // type The type of item.
- void AppendMenuItem(int item_id,
- const std::wstring& label,
- Type type) {
- AppendMenuItemInternal(item_id, label, SkBitmap(), type);
- }
-
- // Append a submenu to this menu.
- // The returned pointer is owned by this menu.
- MenuItemView* AppendSubMenu(int item_id,
- const std::wstring& label) {
- return AppendMenuItemInternal(item_id, label, SkBitmap(), SUBMENU);
- }
-
- // Append a submenu with an icon to this menu.
- // The returned pointer is owned by this menu.
- MenuItemView* AppendSubMenuWithIcon(int item_id,
- const std::wstring& label,
- const SkBitmap& icon) {
- return AppendMenuItemInternal(item_id, label, icon, SUBMENU);
- }
-
- // This is a convenience for standard text label menu items where the label
- // is provided with this call.
- void AppendMenuItemWithLabel(int item_id,
- const std::wstring& label) {
- AppendMenuItem(item_id, label, NORMAL);
- }
-
- // This is a convenience for text label menu items where the label is
- // provided by the delegate.
- void AppendDelegateMenuItem(int item_id) {
- AppendMenuItem(item_id, std::wstring(), NORMAL);
- }
-
- // Adds a separator to this menu
- void AppendSeparator() {
- AppendMenuItemInternal(0, std::wstring(), SkBitmap(), SEPARATOR);
- }
-
- // 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
- // the menu, instead of relying on Windows.
- void AppendMenuItemWithIcon(int item_id,
- const std::wstring& label,
- const SkBitmap& icon) {
- AppendMenuItemInternal(item_id, label, icon, NORMAL);
- }
-
- // Returns the view that contains child menu items. If the submenu has
- // not been creates, this creates it.
- virtual SubmenuView* CreateSubmenu();
-
- // Returns true if this menu item has a submenu.
- virtual bool HasSubmenu() const { return (submenu_ != NULL); }
-
- // Returns the view containing child menu items.
- virtual SubmenuView* GetSubmenu() const { return submenu_; }
-
- // Returns the parent menu item.
- MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }
-
- // Sets the font.
- void SetFont(const gfx::Font& font) { font_ = font; }
-
- // Sets the title
- void SetTitle(const std::wstring& title) {
- title_ = title;
- }
-
- // Returns the title.
- const std::wstring& GetTitle() const { return title_; }
-
- // Sets whether this item is selected. This is invoked as the user moves
- // the mouse around the menu while open.
- void SetSelected(bool selected);
-
- // Returns true if the item is selected.
- bool IsSelected() const { return selected_; }
-
- // Sets the icon for the descendant identified by item_id.
- void SetIcon(const SkBitmap& icon, int item_id);
-
- // Sets the icon of this menu item.
- void SetIcon(const SkBitmap& icon);
-
- // Returns the icon.
- const SkBitmap& GetIcon() const { return icon_; }
-
- // Sets the command id of this menu item.
- void SetCommand(int command) { command_ = command; }
-
- // Returns the command id of this item.
- int GetCommand() const { return command_; }
-
- // Paints the menu item.
- virtual void Paint(gfx::Canvas* canvas);
-
- // Returns the preferred size of this item.
- virtual gfx::Size GetPreferredSize();
-
- // Returns the object responsible for controlling showing the menu.
- MenuController* GetMenuController();
-
- // Returns the delegate. This returns the delegate of the root menu item.
- MenuDelegate* GetDelegate();
-
- // Returns the root parent, or this if this has no parent.
- MenuItemView* GetRootMenuItem();
-
- // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView
- // doesn't have a mnemonic.
- wchar_t GetMnemonic();
-
- // Do we have icons? This only has effect on the top menu. Turning this on
- // makes the menus slightly wider and taller.
- void set_has_icons(bool has_icons) {
- has_icons_ = has_icons;
- }
-
- protected:
- // Creates a MenuItemView. This is used by the various AddXXX methods.
- MenuItemView(MenuItemView* parent, int command, Type type);
-
- private:
- // Called by the two constructors to initialize this menu item.
- void Init(MenuItemView* parent,
- int command,
- MenuItemView::Type type,
- MenuDelegate* delegate);
-
- // All the AddXXX methods funnel into this.
- MenuItemView* AppendMenuItemInternal(int item_id,
- const std::wstring& label,
- const SkBitmap& icon,
- Type type);
-
- // Returns the descendant with the specified command.
- MenuItemView* GetDescendantByID(int id);
-
- // Invoked by the MenuController when the menu closes as the result of
- // drag and drop run.
- void DropMenuClosed(bool notify_delegate);
-
- // The RunXXX methods call into this to set up the necessary state before
- // running.
- void PrepareForRun(bool has_mnemonics);
-
- // Returns the flags passed to DrawStringInt.
- int GetDrawStringFlags();
-
- // If this menu item has no children a child is added showing it has no
- // children. Otherwise AddEmtpyMenuIfNecessary is recursively invoked on
- // child menu items that have children.
- void AddEmptyMenus();
-
- // Undoes the work of AddEmptyMenus.
- void RemoveEmptyMenus();
-
- // Given bounds within our View, this helper routine mirrors the bounds if
- // necessary.
- void AdjustBoundsForRTLUI(gfx::Rect* rect) const;
-
- // Actual paint implementation. If for_drag is true, portions of the menu
- // are not rendered.
- void Paint(gfx::Canvas* canvas, bool for_drag);
-
- // Destroys the window used to display this menu and recursively destroys
- // the windows used to display all descendants.
- void DestroyAllMenuHosts();
-
- // Returns the various margins.
- int GetTopMargin();
- int GetBottomMargin();
-
- // The delegate. This is only valid for the root menu item. You shouldn't
- // use this directly, instead use GetDelegate() which walks the tree as
- // as necessary.
- MenuDelegate* delegate_;
-
- // Returns the controller for the run operation, or NULL if the menu isn't
- // showing.
- MenuController* controller_;
-
- // Used to detect when Cancel was invoked.
- bool canceled_;
-
- // Our parent.
- MenuItemView* parent_menu_item_;
-
- // Type of menu. NOTE: MenuItemView doesn't itself represent SEPARATOR,
- // that is handled by an entirely different view class.
- Type type_;
-
- // Whether we're selected.
- bool selected_;
-
- // Command id.
- int command_;
-
- // Submenu, created via CreateSubmenu.
- SubmenuView* submenu_;
-
- // Font.
- gfx::Font font_;
-
- // Title.
- std::wstring title_;
-
- // Icon.
- SkBitmap icon_;
-
- // Does the title have a mnemonic?
- bool has_mnemonics_;
-
- bool has_icons_;
-
- DISALLOW_EVIL_CONSTRUCTORS(MenuItemView);
-};
-
-// SubmenuView ----------------------------------------------------------------
-
-// SubmenuView is the parent of all menu items.
-//
-// SubmenuView has the following responsibilities:
-// . It positions and sizes all child views (any type of View may be added,
-// not just MenuItemViews).
-// . Forwards the appropriate events to the MenuController. This allows the
-// MenuController to update the selection as the user moves the mouse around.
-// . Renders the drop indicator during a drop operation.
-// . Shows and hides the window (a WidgetWin) when the menu is shown on
-// screen.
-//
-// SubmenuView is itself contained in a MenuScrollViewContainer.
-// MenuScrollViewContainer handles showing as much of the SubmenuView as the
-// screen allows. If the SubmenuView is taller than the screen, scroll buttons
-// are provided that allow the user to see all the menu items.
-class SubmenuView : public View {
- public:
- // Creates a SubmenuView for the specified menu item.
- explicit SubmenuView(MenuItemView* parent);
- ~SubmenuView();
-
- // Returns the number of child views that are MenuItemViews.
- // MenuItemViews are identified by ID.
- int GetMenuItemCount();
-
- // Returns the MenuItemView at the specified index.
- MenuItemView* GetMenuItemAt(int index);
-
- // Positions and sizes the child views. This tiles the views vertically,
- // giving each child the available width.
- virtual void Layout();
- virtual gfx::Size GetPreferredSize();
-
- // View method. Overriden to schedule a paint. We do this so that when
- // scrolling occurs, everything is repainted correctly.
- virtual void DidChangeBounds(const gfx::Rect& previous,
- const gfx::Rect& current);
-
- // Painting.
- void PaintChildren(gfx::Canvas* canvas);
-
- // Drag and drop methods. These are forwarded to the MenuController.
- virtual bool CanDrop(const OSExchangeData& data);
- virtual void OnDragEntered(const DropTargetEvent& event);
- virtual int OnDragUpdated(const DropTargetEvent& event);
- virtual void OnDragExited();
- virtual int OnPerformDrop(const DropTargetEvent& event);
-
- // Scrolls on menu item boundaries.
- virtual bool OnMouseWheel(const MouseWheelEvent& e);
-
- // Returns true if the menu is showing.
- bool IsShowing();
-
- // Shows the menu at the specified location. Coordinates are in screen
- // coordinates. max_width gives the max width the view should be.
- void ShowAt(gfx::NativeView parent, const gfx::Rect& bounds, bool do_capture);
-
- // Closes the menu, destroying the host.
- void Close();
-
- // Hides the hosting window.
- //
- // The hosting window is hidden first, then deleted (Close) when the menu is
- // done running. This is done to avoid deletion ordering dependencies. In
- // particular, during drag and drop (and when a modal dialog is shown as
- // a result of choosing a context menu) it is possible that an event is
- // being processed by the host, so that host is on the stack when we need to
- // close the window. If we closed the window immediately (and deleted it),
- // when control returned back to host we would crash as host was deleted.
- void Hide();
-
- // If mouse capture was grabbed, it is released. Does nothing if mouse was
- // not captured.
- void ReleaseCapture();
-
- // Overriden from View to prevent tab from doing anything.
- virtual bool SkipDefaultKeyEventProcessing(const views::KeyEvent& e);
-
- // Returns the parent menu item we're showing children for.
- MenuItemView* GetMenuItem() const { return parent_menu_item_; }
-
- // Set the drop item and position.
- void SetDropMenuItem(MenuItemView* item,
- MenuDelegate::DropPosition position);
-
- // Returns whether the selection should be shown for the specified item.
- // The selection is NOT shown during drag and drop when the drop is over
- // the menu.
- bool GetShowSelection(MenuItemView* item);
-
- // Returns the container for the SubmenuView.
- MenuScrollViewContainer* GetScrollViewContainer();
-
- // Returns the host of the menu. Returns NULL if not showing.
- MenuHost* host() const { return host_; }
-
- private:
- // Paints the drop indicator. This is only invoked if item is non-NULL and
- // position is not DROP_NONE.
- void PaintDropIndicator(gfx::Canvas* canvas,
- MenuItemView* item,
- MenuDelegate::DropPosition position);
-
- void SchedulePaintForDropIndicator(MenuItemView* item,
- MenuDelegate::DropPosition position);
-
- // Calculates the location of th edrop indicator.
- gfx::Rect CalculateDropIndicatorBounds(MenuItemView* item,
- MenuDelegate::DropPosition position);
-
- // Parent menu item.
- MenuItemView* parent_menu_item_;
-
- // WidgetWin subclass used to show the children.
- MenuHost* host_;
-
- // If non-null, indicates a drop is in progress and drop_item is the item
- // the drop is over.
- MenuItemView* drop_item_;
-
- // Position of the drop.
- MenuDelegate::DropPosition drop_position_;
-
- // Ancestor of the SubmenuView, lazily created.
- MenuScrollViewContainer* scroll_view_container_;
-
- DISALLOW_EVIL_CONSTRUCTORS(SubmenuView);
-};
-
-// MenuController -------------------------------------------------------------
-
-// MenuController manages showing, selecting and drag/drop for menus.
-// All relevant events are forwarded to the MenuController from SubmenuView
-// and MenuHost.
-
-class MenuController
-#if defined(OS_WIN)
- : public MessageLoopForUI::Dispatcher {
-#else
- {
-#endif
- public:
- friend class MenuHostRootView;
- friend class MenuItemView;
-
- // If a menu is currently active, this returns the controller for it.
- static MenuController* GetActiveInstance();
-
- // Runs the menu at the specified location. If the menu was configured to
- // block, the selected item is returned. If the menu does not block this
- // returns NULL immediately.
- MenuItemView* Run(gfx::NativeView parent,
- MenuItemView* root,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition position,
- int* mouse_event_flags);
-
- // Whether or not Run blocks.
- bool IsBlockingRun() const { return blocking_run_; }
-
- // Sets the selection to menu_item, a value of NULL unselects everything.
- // If open_submenu is true and menu_item has a submenu, the submenu is shown.
- // If update_immediately is true, submenus are opened immediately, otherwise
- // submenus are only opened after a timer fires.
- //
- // Internally this updates pending_state_ immediatley, and if
- // update_immediately is true, CommitPendingSelection is invoked to
- // show/hide submenus and update state_.
- void SetSelection(MenuItemView* menu_item,
- bool open_submenu,
- bool update_immediately);
-
- // Cancels the current Run. If all is true, any nested loops are canceled
- // as well. This immediately hides all menus.
- void Cancel(bool all);
-
- // An alternative to Cancel(true) that can be used with a OneShotTimer.
- void CancelAll() { return Cancel(true); }
-
- // Various events, forwarded from the submenu.
- //
- // NOTE: the coordinates of the events are in that of the
- // MenuScrollViewContainer.
- void OnMousePressed(SubmenuView* source, const MouseEvent& event);
- void OnMouseDragged(SubmenuView* source, const MouseEvent& event);
- void OnMouseReleased(SubmenuView* source, const MouseEvent& event);
- void OnMouseMoved(SubmenuView* source, const MouseEvent& event);
- void OnMouseEntered(SubmenuView* source, const MouseEvent& event);
- bool CanDrop(SubmenuView* source, const OSExchangeData& data);
- void OnDragEntered(SubmenuView* source, const DropTargetEvent& event);
- int OnDragUpdated(SubmenuView* source, const DropTargetEvent& event);
- void OnDragExited(SubmenuView* source);
- int OnPerformDrop(SubmenuView* source, const DropTargetEvent& event);
-
- // Invoked from the scroll buttons of the MenuScrollViewContainer.
- void OnDragEnteredScrollButton(SubmenuView* source, bool is_up);
- void OnDragExitedScrollButton(SubmenuView* source);
-
- private:
- class MenuScrollTask;
-
- // Tracks selection information.
- struct State {
- State() : item(NULL), submenu_open(false) {}
-
- // The selected menu item.
- MenuItemView* item;
-
- // If item has a submenu this indicates if the submenu is showing.
- bool submenu_open;
-
- // Bounds passed to the run menu. Used for positioning the first menu.
- gfx::Rect initial_bounds;
-
- // Position of the initial menu.
- MenuItemView::AnchorPosition anchor;
-
- // The direction child menus have opened in.
- std::list<bool> open_leading;
-
- // Bounds for the monitor we're showing on.
- gfx::Rect monitor_bounds;
- };
-
- // Used by GetMenuPartByScreenCoordinate to indicate the menu part at a
- // particular location.
- struct MenuPart {
- // Type of part.
- enum Type {
- NONE,
- MENU_ITEM,
- SCROLL_UP,
- SCROLL_DOWN
- };
-
- MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {}
-
- // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
- bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }
-
- // Type of part.
- Type type;
-
- // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
- // this is NULL.
- // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item
- // but is over a menu (for example, the mouse is over a separator or
- // empty menu), this is NULL and parent is the menu the mouse was
- // clicked on.
- MenuItemView* menu;
-
- // If type is MENU_ITEM but the mouse is not over a menu item this is the
- // parent of the menu item the user clicked on. Otherwise this is NULL.
- MenuItemView* parent;
-
- // If type is SCROLL_*, this is the submenu the mouse is over.
- SubmenuView* submenu;
- };
-
- // Sets the active MenuController.
- static void SetActiveInstance(MenuController* controller);
-
-#if defined(OS_WIN)
- // Dispatcher method. This returns true if the menu was canceled, or
- // if the message is such that the menu should be closed.
- virtual bool Dispatch(const MSG& msg);
-
- // Key processing. The return value of these is returned from Dispatch.
- // In other words, if these return false (which they do if escape was
- // pressed, or a matching mnemonic was found) the message loop returns.
- bool OnKeyDown(const MSG& msg);
- bool OnChar(const MSG& msg);
-#endif
-
- // Creates a MenuController. If blocking is true, Run blocks the caller
- explicit MenuController(bool blocking);
-
- ~MenuController();
-
- // Invoked when the user accepts the selected item. This is only used
- // when blocking. This schedules the loop to quit.
- void Accept(MenuItemView* item, int mouse_event_flags);
-
- // Closes all menus, including any menus of nested invocations of Run.
- void CloseAllNestedMenus();
-
- // Gets the enabled menu item at the specified location.
- // If over_any_menu is non-null it is set to indicate whether the location
- // is over any menu. It is possible for this to return NULL, but
- // over_any_menu to be true. For example, the user clicked on a separator.
- MenuItemView* GetMenuItemAt(View* menu, int x, int y);
-
- // If there is an empty menu item at the specified location, it is returned.
- MenuItemView* GetEmptyMenuItemAt(View* source, int x, int y);
-
- // Returns true if the coordinate is over the scroll buttons of the
- // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to
- // indicate which scroll button the coordinate is.
- bool IsScrollButtonAt(SubmenuView* source,
- int x,
- int y,
- MenuPart::Type* part);
-
- // Returns the target for the mouse event.
- MenuPart GetMenuPartByScreenCoordinate(SubmenuView* source,
- int source_x,
- int source_y);
-
- // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns
- // true if the supplied SubmenuView contains the location in terms of the
- // screen. If it does, part is set appropriately and true is returned.
- bool GetMenuPartByScreenCoordinateImpl(SubmenuView* menu,
- const gfx::Point& screen_loc,
- MenuPart* part);
-
- // Returns true if the SubmenuView contains the specified location. This does
- // NOT included the scroll buttons, only the submenu view.
- bool DoesSubmenuContainLocation(SubmenuView* submenu,
- const gfx::Point& screen_loc);
-
- // Opens/Closes the necessary menus such that state_ matches that of
- // pending_state_. This is invoked if submenus are not opened immediately,
- // but after a delay.
- void CommitPendingSelection();
-
- // If item has a submenu, it is closed. This does NOT update the selection
- // in anyway.
- void CloseMenu(MenuItemView* item);
-
- // If item has a submenu, it is opened. This does NOT update the selection
- // in anyway.
- void OpenMenu(MenuItemView* item);
-
- // Builds the paths of the two menu items into the two paths, and
- // sets first_diff_at to the location of the first difference between the
- // two paths.
- void BuildPathsAndCalculateDiff(MenuItemView* old_item,
- MenuItemView* new_item,
- std::vector<MenuItemView*>* old_path,
- std::vector<MenuItemView*>* new_path,
- size_t* first_diff_at);
-
- // Builds the path for the specified item.
- void BuildMenuItemPath(MenuItemView* item, std::vector<MenuItemView*>* path);
-
- // Starts/stops the timer that commits the pending state to state
- // (opens/closes submenus).
- void StartShowTimer();
- void StopShowTimer();
-
- // Starts/stops the timer cancel the menu. This is used during drag and
- // drop when the drop enters/exits the menu.
- void StartCancelAllTimer();
- void StopCancelAllTimer();
-
- // Calculates the bounds of the menu to show. is_leading is set to match the
- // direction the menu opened in.
- gfx::Rect CalculateMenuBounds(MenuItemView* item,
- bool prefer_leading,
- bool* is_leading);
-
- // Returns the depth of the menu.
- static int MenuDepth(MenuItemView* item);
-
- // Selects the next/previous menu item.
- void IncrementSelection(int delta);
-
- // If the selected item has a submenu and it isn't currently open, the
- // the selection is changed such that the menu opens immediately.
- void OpenSubmenuChangeSelectionIfCan();
-
- // If possible, closes the submenu.
- void CloseSubmenu();
-
- // Returns true if window is the window used to show item, or any of
- // items ancestors.
- bool IsMenuWindow(MenuItemView* item, gfx::NativeWindow window);
-
- // Selects by mnemonic, and if that doesn't work tries the first character of
- // the title. Returns true if a match was selected and the menu should exit.
- bool SelectByChar(wchar_t key);
-
- // If there is a window at the location of the event, a new mouse event is
- // generated and posted to it.
- void RepostEvent(SubmenuView* source, const MouseEvent& event);
-
- // Sets the drop target to new_item.
- void SetDropMenuItem(MenuItemView* new_item,
- MenuDelegate::DropPosition position);
-
- // Starts/stops scrolling as appropriate. part gives the part the mouse is
- // over.
- void UpdateScrolling(const MenuPart& part);
-
- // Stops scrolling.
- void StopScrolling();
-
- // The active instance.
- static MenuController* active_instance_;
-
- // If true, Run blocks. If false, Run doesn't block and this is used for
- // drag and drop. Note that the semantics for drag and drop are slightly
- // different: cancel timer is kicked off any time the drag moves outside the
- // menu, mouse events do nothing...
- bool blocking_run_;
-
- // If true, we're showing.
- bool showing_;
-
- // If true, all nested run loops should be exited.
- bool exit_all_;
-
- // Whether we did a capture. We do a capture only if we're blocking and
- // the mouse was down when Run.
- bool did_capture_;
-
- // As the user drags the mouse around pending_state_ changes immediately.
- // When the user stops moving/dragging the mouse (or clicks the mouse)
- // pending_state_ is committed to state_, potentially resulting in
- // opening or closing submenus. This gives a slight delayed effect to
- // submenus as the user moves the mouse around. This is done so that as the
- // user moves the mouse all submenus don't immediately pop.
- State pending_state_;
- State state_;
-
- // If the user accepted the selection, this is the result.
- MenuItemView* result_;
-
- // The mouse event flags when the user clicked on a menu. Is 0 if the
- // user did not use the mousee to select the menu.
- int result_mouse_event_flags_;
-
- // If not empty, it means we're nested. When Run is invoked from within
- // Run, the current state (state_) is pushed onto menu_stack_. This allows
- // MenuController to restore the state when the nested run returns.
- std::list<State> menu_stack_;
-
- // As the mouse moves around submenus are not opened immediately. Instead
- // they open after this timer fires.
- base::OneShotTimer<MenuController> show_timer_;
-
- // Used to invoke CancelAll(). This is used during drag and drop to hide the
- // menu after the mouse moves out of the of the menu. This is necessitated by
- // the lack of an ability to detect when the drag has completed from the drop
- // side.
- base::OneShotTimer<MenuController> cancel_all_timer_;
-
- // Drop target.
- MenuItemView* drop_target_;
- MenuDelegate::DropPosition drop_position_;
-
- // Owner of child windows.
- gfx::NativeWindow owner_;
-
- // Indicates a possible drag operation.
- bool possible_drag_;
-
- // Location the mouse was pressed at. Used to detect d&d.
- int press_x_;
- int press_y_;
-
- // We get a slew of drag updated messages as the mouse is over us. To avoid
- // continually processing whether we can drop, we cache the coordinates.
- bool valid_drop_coordinates_;
- int drop_x_;
- int drop_y_;
- int last_drop_operation_;
-
- // If true, we're in the middle of invoking ShowAt on a submenu.
- bool showing_submenu_;
-
- // Task for scrolling the menu. If non-null indicates a scroll is currently
- // underway.
- scoped_ptr<MenuScrollTask> scroll_task_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuController);
-};
-
-} // namespace views
-
-#endif // VIEWS_CONTROLS_MENU_CHROME_MENU_H_
diff --git a/views/controls/menu/menu_config.cc b/views/controls/menu/menu_config.cc
new file mode 100644
index 0000000..cfdebea
--- /dev/null
+++ b/views/controls/menu/menu_config.cc
@@ -0,0 +1,25 @@
+// 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/menu_config.h"
+
+#include "build/build_config.h"
+
+namespace views {
+
+static MenuConfig* config_instance = NULL;
+
+void MenuConfig::Reset() {
+ delete config_instance;
+ config_instance = NULL;
+}
+
+// static
+const MenuConfig& MenuConfig::instance() {
+ if (!config_instance)
+ config_instance = Create();
+ return *config_instance;
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_config.h b/views/controls/menu/menu_config.h
new file mode 100644
index 0000000..a502b8a
--- /dev/null
+++ b/views/controls/menu/menu_config.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef VIEWS_CONTROLS_MENU_MENU_CONFIG_H_
+#define VIEWS_CONTROLS_MENU_MENU_CONFIG_H_
+
+#include "app/gfx/font.h"
+
+namespace views {
+
+// Layout type information for menu items. Use the instance() method to obtain
+// the MenuConfig for the current platform.
+struct MenuConfig {
+ MenuConfig() : item_top_margin(3),
+ item_bottom_margin(4),
+ item_no_icon_top_margin(1),
+ item_no_icon_bottom_margin(3),
+ item_left_margin(4),
+ label_to_arrow_padding(10),
+ arrow_to_edge_padding(5),
+ icon_to_label_padding(8),
+ gutter_to_label(5),
+ check_width(16),
+ check_height(16),
+ arrow_height(9),
+ arrow_width(9),
+ gutter_width(0),
+ separator_height(6),
+ render_gutter(false),
+ show_mnemonics(false),
+ scroll_arrow_height(3) {
+ }
+
+ // Resets the single shared MenuConfig instance. The next time instance() is
+ // invoked a new MenuConfig is created and configured.
+ static void Reset();
+
+ // Returns the single shared MenuConfig instance, creating if necessary.
+ static const MenuConfig& instance();
+
+ // Font used by menus.
+ gfx::Font font;
+
+ // Margins between the top of the item and the label.
+ int item_top_margin;
+
+ // Margins between the bottom of the item and the label.
+ int item_bottom_margin;
+
+ // Margins used if the menu doesn't have icons.
+ int item_no_icon_top_margin;
+ int item_no_icon_bottom_margin;
+
+ // Margins between the left of the item and the icon.
+ int item_left_margin;
+
+ // Padding between the label and submenu arrow.
+ int label_to_arrow_padding;
+
+ // Padding between the arrow and the edge.
+ int arrow_to_edge_padding;
+
+ // Padding between the icon and label.
+ int icon_to_label_padding;
+
+ // Padding between the gutter and label.
+ int gutter_to_label;
+
+ // Size of the check.
+ int check_width;
+ int check_height;
+
+ // Size of the submenu arrow.
+ int arrow_height;
+ int arrow_width;
+
+ // Width of the gutter. Only used if render_gutter is true.
+ int gutter_width;
+
+ // Height of the separator.
+ int separator_height;
+
+ // Whether or not the gutter should be rendered. The gutter is specific to
+ // Vista.
+ bool render_gutter;
+
+ // Are mnemonics shown?
+ bool show_mnemonics;
+
+ // Height of the scroll arrow.
+ int scroll_arrow_height;
+
+ private:
+ // Creates and configures a new MenuConfig as appropriate for the current
+ // platform.
+ static MenuConfig* Create();
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_MENU_CONFIG_H_
diff --git a/views/controls/menu/menu_config_gtk.cc b/views/controls/menu/menu_config_gtk.cc
new file mode 100644
index 0000000..1930824
--- /dev/null
+++ b/views/controls/menu/menu_config_gtk.cc
@@ -0,0 +1,15 @@
+// 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/menu_config.h"
+
+namespace views {
+
+// static
+MenuConfig* MenuConfig::Create() {
+ // TODO: decide what we want this to look like.
+ return new MenuConfig();
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_config_win.cc b/views/controls/menu/menu_config_win.cc
new file mode 100644
index 0000000..233eae68
--- /dev/null
+++ b/views/controls/menu/menu_config_win.cc
@@ -0,0 +1,84 @@
+// 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/menu_config.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <Vssym32.h>
+
+#include "base/gfx/native_theme.h"
+#include "base/logging.h"
+#include "app/l10n_util_win.h"
+#include "base/win_util.h"
+
+using gfx::NativeTheme;
+
+namespace views {
+
+// static
+MenuConfig* MenuConfig::Create() {
+ MenuConfig* config = new MenuConfig();
+ NONCLIENTMETRICS metrics;
+ win_util::GetNonClientMetrics(&metrics);
+ l10n_util::AdjustUIFont(&(metrics.lfMenuFont));
+ HFONT font = CreateFontIndirect(&metrics.lfMenuFont);
+ DLOG_ASSERT(font);
+ config->font = gfx::Font::CreateFont(font);
+
+ HDC dc = GetDC(NULL);
+ RECT bounds = { 0, 0, 200, 200 };
+ SIZE check_size;
+ if (NativeTheme::instance()->GetThemePartSize(
+ NativeTheme::MENU, dc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, &bounds,
+ TS_TRUE, &check_size) == S_OK) {
+ config->check_width = check_size.cx;
+ config->check_height = check_size.cy;
+ } else {
+ config->check_width = GetSystemMetrics(SM_CXMENUCHECK);
+ config->check_height = GetSystemMetrics(SM_CYMENUCHECK);
+ }
+
+ SIZE arrow_size;
+ if (NativeTheme::instance()->GetThemePartSize(
+ NativeTheme::MENU, dc, MENU_POPUPSUBMENU, MSM_NORMAL, &bounds,
+ TS_TRUE, &arrow_size) == S_OK) {
+ config->arrow_width = arrow_size.cx;
+ config->arrow_height = arrow_size.cy;
+ } else {
+ // Sadly I didn't see a specify metrics for this.
+ config->arrow_width = GetSystemMetrics(SM_CXMENUCHECK);
+ config->arrow_height = GetSystemMetrics(SM_CYMENUCHECK);
+ }
+
+ SIZE gutter_size;
+ if (NativeTheme::instance()->GetThemePartSize(
+ NativeTheme::MENU, dc, MENU_POPUPGUTTER, MSM_NORMAL, &bounds,
+ TS_TRUE, &gutter_size) == S_OK) {
+ config->gutter_width = gutter_size.cx;
+ config->render_gutter = true;
+ } else {
+ config->gutter_width = 0;
+ config->render_gutter = false;
+ }
+
+ SIZE separator_size;
+ if (NativeTheme::instance()->GetThemePartSize(
+ NativeTheme::MENU, dc, MENU_POPUPSEPARATOR, MSM_NORMAL, &bounds,
+ TS_TRUE, &separator_size) == S_OK) {
+ config->separator_height = separator_size.cy;
+ } else {
+ config->separator_height = GetSystemMetrics(SM_CYMENU) / 2;
+ }
+
+ ReleaseDC(NULL, dc);
+
+ BOOL show_cues;
+ config->show_mnemonics =
+ (SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &show_cues, 0) &&
+ show_cues == TRUE);
+ return config;
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc
index f8efbbc..7c54aa4 100644
--- a/views/controls/menu/menu_controller.cc
+++ b/views/controls/menu/menu_controller.cc
@@ -7,7 +7,6 @@
#include "app/gfx/canvas.h"
#include "app/l10n_util.h"
#include "app/os_exchange_data.h"
-#include "base/base_drag_source.h"
#include "base/time.h"
#include "views/controls/menu/menu_scroll_view_container.h"
#include "views/controls/menu/submenu_view.h"
@@ -17,6 +16,7 @@
#if defined(OS_WIN)
#include "app/os_exchange_data_provider_win.h"
+#include "base/base_drag_source.h"
#endif
using base::Time;
@@ -144,7 +144,7 @@ static int instance_count = 0;
static int nested_depth = 0;
#endif
-MenuItemView* MenuController::Run(HWND parent,
+MenuItemView* MenuController::Run(gfx::NativeView parent,
MenuItemView* root,
const gfx::Rect& bounds,
MenuItemView::AnchorPosition position,
@@ -179,8 +179,10 @@ MenuItemView* MenuController::Run(HWND parent,
pending_state_.anchor = position;
owner_ = parent;
+ // TODO: push this into Screen.
// Calculate the bounds of the monitor we'll show menus on. Do this once to
// avoid repeated system queries for the info.
+#if defined(OS_WIN)
POINT initial_loc = { bounds.x(), bounds.y() };
HMONITOR monitor = MonitorFromPoint(initial_loc, MONITOR_DEFAULTTONEAREST);
if (monitor) {
@@ -190,6 +192,9 @@ MenuItemView* MenuController::Run(HWND parent,
// Menus appear over the taskbar.
pending_state_.monitor_bounds = gfx::Rect(mi.rcMonitor);
}
+#else
+ NOTIMPLEMENTED();
+#endif
// Set the selection, which opens the initial menu.
SetSelection(root, true, true);
@@ -338,7 +343,13 @@ void MenuController::OnMousePressed(SubmenuView* source,
// We're going to close and we own the mouse capture. We need to repost the
// mouse down, otherwise the window the user clicked on won't get the
// event.
+#if defined(OS_WIN)
RepostEvent(source, event);
+#else
+ // Do we really need the repost logic for linux? I tend to think not but I
+ // need to verify that
+ NOTIMPLEMENTED();
+#endif
// And close.
Cancel(true);
@@ -398,13 +409,17 @@ void MenuController::OnMouseDragged(SubmenuView* source,
item->height(), press_loc.x(),
press_loc.y(), &data);
- scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
+ StopScrolling();
+#if defined(OS_WIN)
int drag_ops = item->GetDelegate()->GetDragOperations(item);
+ scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
DWORD effects;
- StopScrolling();
DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source,
DragDropTypes::DragOperationToDropEffect(drag_ops),
&effects);
+#else
+ NOTIMPLEMENTED();
+#endif
if (GetActiveInstance() == this) {
if (showing_) {
// We're still showing, close all menus.
@@ -637,6 +652,7 @@ void MenuController::SetActiveInstance(MenuController* controller) {
active_instance_ = controller;
}
+#if defined(OS_WIN)
bool MenuController::Dispatch(const MSG& msg) {
DCHECK(blocking_run_);
@@ -759,6 +775,16 @@ bool MenuController::OnChar(const MSG& msg) {
return !SelectByChar(static_cast<wchar_t>(msg.wParam));
}
+#else
+bool MenuController::Dispatch(GdkEvent* event) {
+ gtk_main_do_event(event);
+ if (exit_all_)
+ return false;
+
+ NOTIMPLEMENTED();
+ return !exit_all_;
+}
+#endif
MenuController::MenuController(bool blocking)
: blocking_run_(blocking),
@@ -766,12 +792,12 @@ MenuController::MenuController(bool blocking)
exit_all_(false),
did_capture_(false),
result_(NULL),
+ result_mouse_event_flags_(0),
drop_target_(NULL),
owner_(NULL),
possible_drag_(false),
valid_drop_coordinates_(false),
- showing_submenu_(false),
- result_mouse_event_flags_(0) {
+ showing_submenu_(false) {
#ifdef DEBUG_MENU
instance_count++;
DLOG(INFO) << "created MC, count=" << instance_count;
@@ -1222,14 +1248,6 @@ void MenuController::CloseSubmenu() {
}
}
-bool MenuController::IsMenuWindow(MenuItemView* item, HWND window) {
- if (!item)
- return false;
- return ((item->HasSubmenu() && item->GetSubmenu()->IsShowing() &&
- item->GetSubmenu()->GetWidget()->GetNativeView() == window) ||
- IsMenuWindow(item->GetParentMenuItem(), window));
-}
-
bool MenuController::SelectByChar(wchar_t character) {
wchar_t char_array[1] = { character };
wchar_t key = l10n_util::ToLower(char_array)[0];
@@ -1290,6 +1308,7 @@ bool MenuController::SelectByChar(wchar_t character) {
return false;
}
+#if defined(OS_WIN)
void MenuController::RepostEvent(SubmenuView* source,
const MouseEvent& event) {
gfx::Point screen_loc(event.location());
@@ -1352,6 +1371,7 @@ void MenuController::RepostEvent(SubmenuView* source,
}
}
}
+#endif
void MenuController::SetDropMenuItem(
MenuItemView* new_target,
diff --git a/views/controls/menu/menu_controller.h b/views/controls/menu/menu_controller.h
index 16034e1..2512ede 100644
--- a/views/controls/menu/menu_controller.h
+++ b/views/controls/menu/menu_controller.h
@@ -31,12 +31,7 @@ class View;
// MenuController is used internally by the various menu classes to manage
// showing, selecting and drag/drop for menus. All relevant events are
// forwarded to the MenuController from SubmenuView and MenuHost.
-class MenuController
-#if defined(OS_WIN)
- : public MessageLoopForUI::Dispatcher {
-#else
- {
-#endif
+class MenuController : public MessageLoopForUI::Dispatcher {
public:
friend class MenuHostRootView;
friend class MenuItemView;
@@ -168,6 +163,8 @@ class MenuController
// pressed, or a matching mnemonic was found) the message loop returns.
bool OnKeyDown(const MSG& msg);
bool OnChar(const MSG& msg);
+#else
+ virtual bool Dispatch(GdkEvent* event);
#endif
// Creates a MenuController. If blocking is true, Run blocks the caller
@@ -270,17 +267,15 @@ class MenuController
// If possible, closes the submenu.
void CloseSubmenu();
- // Returns true if window is the window used to show item, or any of
- // items ancestors.
- bool IsMenuWindow(MenuItemView* item, gfx::NativeWindow window);
-
// Selects by mnemonic, and if that doesn't work tries the first character of
// the title. Returns true if a match was selected and the menu should exit.
bool SelectByChar(wchar_t key);
+#if defined(OS_WIN)
// If there is a window at the location of the event, a new mouse event is
// generated and posted to it.
void RepostEvent(SubmenuView* source, const MouseEvent& event);
+#endif
// Sets the drop target to new_item.
void SetDropMenuItem(MenuItemView* new_item,
@@ -348,7 +343,7 @@ class MenuController
MenuDelegate::DropPosition drop_position_;
// Owner of child windows.
- gfx::NativeWindow owner_;
+ gfx::NativeView owner_;
// Indicates a possible drag operation.
bool possible_drag_;
diff --git a/views/controls/menu/menu_host_gtk.cc b/views/controls/menu/menu_host_gtk.cc
new file mode 100644
index 0000000..b87aa23
--- /dev/null
+++ b/views/controls/menu/menu_host_gtk.cc
@@ -0,0 +1,99 @@
+// 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/menu_host_gtk.h"
+
+#include <gdk/gdk.h>
+
+#include "views/controls/menu/menu_controller.h"
+#include "views/controls/menu/menu_host_root_view.h"
+#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/submenu_view.h"
+
+namespace views {
+
+MenuHost::MenuHost(SubmenuView* submenu)
+ : WidgetGtk(WidgetGtk::TYPE_POPUP),
+ closed_(false),
+ submenu_(submenu) {
+ // TODO(sky): make sure this is needed.
+ GdkModifierType current_event_mod;
+ if (gtk_get_current_event_state(&current_event_mod)) {
+ set_mouse_down(
+ (current_event_mod & GDK_BUTTON1_MASK) ||
+ (current_event_mod & GDK_BUTTON2_MASK) ||
+ (current_event_mod & GDK_BUTTON3_MASK) ||
+ (current_event_mod & GDK_BUTTON4_MASK) ||
+ (current_event_mod & GDK_BUTTON5_MASK));
+ }
+}
+
+void MenuHost::Init(gfx::NativeView parent,
+ const gfx::Rect& bounds,
+ View* contents_view,
+ bool do_capture) {
+ WidgetGtk::Init(parent, bounds);
+ SetContentsView(contents_view);
+ // TODO(sky): see if there is some way to show without changing focus.
+ Show();
+ if (do_capture) {
+ DoGrab();
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "Doing capture";
+#endif
+ }
+}
+
+void MenuHost::Show() {
+ WidgetGtk::Show();
+}
+
+void MenuHost::Hide() {
+ if (closed_) {
+ // We're already closed, nothing to do.
+ // This is invoked twice if the first time just hid us, and the second
+ // time deleted Closed (deleted) us.
+ return;
+ }
+ // The menus are freed separately, and possibly before the window is closed,
+ // remove them so that View doesn't try to access deleted objects.
+ static_cast<MenuHostRootView*>(GetRootView())->suspend_events();
+ GetRootView()->RemoveAllChildViews(false);
+ closed_ = true;
+ ReleaseGrab();
+ WidgetGtk::Hide();
+}
+
+void MenuHost::HideWindow() {
+ // Make sure we release capture before hiding.
+ ReleaseGrab();
+ WidgetGtk::Hide();
+}
+
+void MenuHost::ReleaseCapture() {
+ ReleaseGrab();
+}
+
+RootView* MenuHost::CreateRootView() {
+ return new MenuHostRootView(this, submenu_);
+}
+
+void MenuHost::OnCancelMode() {
+ // TODO(sky): see if there is an equivalent to this.
+ if (!closed_) {
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "OnCanceMode, closing menu";
+#endif
+ submenu_->GetMenuItem()->GetMenuController()->Cancel(true);
+ }
+}
+
+// Overriden to return false, we do NOT want to release capture on mouse
+// release.
+bool MenuHost::ReleaseCaptureOnMouseReleased() {
+ return false;
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_host_gtk.h b/views/controls/menu/menu_host_gtk.h
new file mode 100644
index 0000000..545acb5
--- /dev/null
+++ b/views/controls/menu/menu_host_gtk.h
@@ -0,0 +1,51 @@
+// 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.
+
+
+#ifndef VIEWS_CONTROLS_MENU_MENU_HOST_GTK_H_
+#define VIEWS_CONTROLS_MENU_MENU_HOST_GTK_H_
+
+#include "views/widget/widget_gtk.h"
+
+namespace views {
+
+class SubmenuView;
+
+// MenuHost implementation for Gtk.
+class MenuHost : public WidgetGtk {
+ public:
+ explicit MenuHost(SubmenuView* submenu);
+
+ void Init(gfx::NativeView parent,
+ const gfx::Rect& bounds,
+ View* contents_view,
+ bool do_capture);
+
+ void Show();
+ virtual void Hide();
+ virtual void HideWindow();
+ void ReleaseCapture();
+
+ protected:
+ virtual RootView* CreateRootView();
+
+ virtual void OnCancelMode();
+
+ // Overriden to return false, we do NOT want to release capture on mouse
+ // release.
+ virtual bool ReleaseCaptureOnMouseReleased();
+
+ private:
+ // If true, we've been closed.
+ bool closed_;
+
+ // The view we contain.
+ SubmenuView* submenu_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuHost);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_MENU_HOST_GTK_H_
diff --git a/views/controls/menu/menu_host_root_view.cc b/views/controls/menu/menu_host_root_view.cc
new file mode 100644
index 0000000..1812801
--- /dev/null
+++ b/views/controls/menu/menu_host_root_view.cc
@@ -0,0 +1,92 @@
+// 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/menu_host_root_view.h"
+
+#include "views/controls/menu/menu_controller.h"
+#include "views/controls/menu/submenu_view.h"
+
+namespace views {
+
+MenuHostRootView::MenuHostRootView(Widget* widget,
+ SubmenuView* submenu)
+ : RootView(widget),
+ submenu_(submenu),
+ forward_drag_to_menu_controller_(true),
+ suspend_events_(false) {
+#ifdef DEBUG_MENU
+ DLOG(INFO) << " new MenuHostRootView " << this;
+#endif
+}
+
+bool MenuHostRootView::OnMousePressed(const MouseEvent& event) {
+ if (suspend_events_)
+ return true;
+
+ forward_drag_to_menu_controller_ =
+ ((event.x() < 0 || event.y() < 0 || event.x() >= width() ||
+ event.y() >= height()) ||
+ !RootView::OnMousePressed(event));
+ if (forward_drag_to_menu_controller_)
+ GetMenuController()->OnMousePressed(submenu_, event);
+ return true;
+}
+
+bool MenuHostRootView::OnMouseDragged(const MouseEvent& event) {
+ if (suspend_events_)
+ return true;
+
+ if (forward_drag_to_menu_controller_) {
+#ifdef DEBUG_MENU
+ DLOG(INFO) << " MenuHostRootView::OnMouseDragged source=" << submenu_;
+#endif
+ GetMenuController()->OnMouseDragged(submenu_, event);
+ return true;
+ }
+ return RootView::OnMouseDragged(event);
+}
+
+void MenuHostRootView::OnMouseReleased(const MouseEvent& event,
+ bool canceled) {
+ if (suspend_events_)
+ return;
+
+ RootView::OnMouseReleased(event, canceled);
+ if (forward_drag_to_menu_controller_) {
+ forward_drag_to_menu_controller_ = false;
+ if (canceled) {
+ GetMenuController()->Cancel(true);
+ } else {
+ GetMenuController()->OnMouseReleased(submenu_, event);
+ }
+ }
+}
+
+void MenuHostRootView::OnMouseMoved(const MouseEvent& event) {
+ if (suspend_events_)
+ return;
+
+ RootView::OnMouseMoved(event);
+ GetMenuController()->OnMouseMoved(submenu_, event);
+}
+
+void MenuHostRootView::ProcessOnMouseExited() {
+ if (suspend_events_)
+ return;
+
+ RootView::ProcessOnMouseExited();
+}
+
+bool MenuHostRootView::ProcessMouseWheelEvent(const MouseWheelEvent& e) {
+ // RootView::ProcessMouseWheelEvent forwards to the focused view. We don't
+ // have a focused view, so we need to override this then forward to
+ // the menu.
+ return submenu_->OnMouseWheel(e);
+}
+
+MenuController* MenuHostRootView::GetMenuController() {
+ return submenu_->GetMenuItem()->GetMenuController();
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_host_root_view.h b/views/controls/menu/menu_host_root_view.h
new file mode 100644
index 0000000..2ae774e
--- /dev/null
+++ b/views/controls/menu/menu_host_root_view.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef VIEWS_CONTROLS_MENU_MENU_HOST_ROOT_VIEW_H_
+#define VIEWS_CONTROLS_MENU_MENU_HOST_ROOT_VIEW_H_
+
+#include "views/widget/root_view.h"
+
+namespace views {
+
+class MenuController;
+class SubmenuView;
+
+// MenuHostRootView is the RootView of the window showing the menu.
+// SubmenuView's scroll view is added as a child of MenuHostRootView.
+// MenuHostRootView forwards relevant events to the MenuController.
+//
+// As all the menu items are owned by the root menu item, care must be taken
+// such that when MenuHostRootView is deleted it doesn't delete the menu items.
+class MenuHostRootView : public RootView {
+ public:
+ MenuHostRootView(Widget* widget, SubmenuView* submenu);
+
+ // When invoked subsequent events are NOT forwarded to the MenuController.
+ void suspend_events() {
+ suspend_events_ = true;
+ }
+
+ virtual bool OnMousePressed(const MouseEvent& event);
+ virtual bool OnMouseDragged(const MouseEvent& event);
+ virtual void OnMouseReleased(const MouseEvent& event, bool canceled);
+ virtual void OnMouseMoved(const MouseEvent& event);
+ virtual void ProcessOnMouseExited();
+ virtual bool ProcessMouseWheelEvent(const MouseWheelEvent& e);
+
+ private:
+ // Returns the MenuController for this MenuHostRootView.
+ MenuController* GetMenuController();
+
+ // The SubmenuView we contain.
+ SubmenuView* submenu_;
+
+ // Whether mouse dragged/released should be forwarded to the MenuController.
+ bool forward_drag_to_menu_controller_;
+
+ // Whether events are suspended. If true, no events are forwarded to the
+ // MenuController.
+ bool suspend_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuHostRootView);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_MENU_HOST_ROOT_VIEW_H_
diff --git a/views/controls/menu/menu_host_win.cc b/views/controls/menu/menu_host_win.cc
new file mode 100644
index 0000000..d6fd655
--- /dev/null
+++ b/views/controls/menu/menu_host_win.cc
@@ -0,0 +1,114 @@
+// 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/menu_host_win.h"
+
+#include "base/win_util.h"
+#include "views/controls/menu/menu_controller.h"
+#include "views/controls/menu/menu_host_root_view.h"
+#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/submenu_view.h"
+
+namespace views {
+
+MenuHost::MenuHost(SubmenuView* submenu)
+ : closed_(false),
+ submenu_(submenu),
+ owns_capture_(false) {
+ set_window_style(WS_POPUP);
+ set_initial_class_style(
+ (win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
+ 0 : CS_DROPSHADOW);
+ is_mouse_down_ =
+ ((GetKeyState(VK_LBUTTON) & 0x80) ||
+ (GetKeyState(VK_RBUTTON) & 0x80) ||
+ (GetKeyState(VK_MBUTTON) & 0x80) ||
+ (GetKeyState(VK_XBUTTON1) & 0x80) ||
+ (GetKeyState(VK_XBUTTON2) & 0x80));
+ // Mouse clicks shouldn't give us focus.
+ set_window_ex_style(WS_EX_TOPMOST | WS_EX_NOACTIVATE);
+}
+
+void MenuHost::Init(HWND parent,
+ const gfx::Rect& bounds,
+ View* contents_view,
+ bool do_capture) {
+ WidgetWin::Init(parent, bounds);
+ SetContentsView(contents_view);
+ Show();
+ owns_capture_ = do_capture;
+ if (do_capture) {
+ SetCapture();
+ has_capture_ = true;
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "Doing capture";
+#endif
+ }
+}
+
+void MenuHost::Show() {
+ // We don't want to take focus away from the hosting window.
+ ShowWindow(SW_SHOWNA);
+}
+
+void MenuHost::Hide() {
+ if (closed_) {
+ // We're already closed, nothing to do.
+ // This is invoked twice if the first time just hid us, and the second
+ // time deleted Closed (deleted) us.
+ return;
+ }
+ // The menus are freed separately, and possibly before the window is closed,
+ // remove them so that View doesn't try to access deleted objects.
+ static_cast<MenuHostRootView*>(GetRootView())->suspend_events();
+ GetRootView()->RemoveAllChildViews(false);
+ closed_ = true;
+ ReleaseCapture();
+ WidgetWin::Hide();
+}
+
+void MenuHost::HideWindow() {
+ // Make sure we release capture before hiding.
+ ReleaseCapture();
+ WidgetWin::Hide();
+}
+
+void MenuHost::OnCaptureChanged(HWND hwnd) {
+ WidgetWin::OnCaptureChanged(hwnd);
+ owns_capture_ = false;
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "Capture changed";
+#endif
+}
+
+void MenuHost::ReleaseCapture() {
+ if (owns_capture_) {
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "released capture";
+#endif
+ owns_capture_ = false;
+ ::ReleaseCapture();
+ }
+}
+
+RootView* MenuHost::CreateRootView() {
+ return new MenuHostRootView(this, submenu_);
+}
+
+void MenuHost::OnCancelMode() {
+ if (!closed_) {
+#ifdef DEBUG_MENU
+ DLOG(INFO) << "OnCanceMode, closing menu";
+#endif
+ submenu_->GetMenuItem()->GetMenuController()->Cancel(true);
+ }
+}
+
+// Overriden to return false, we do NOT want to release capture on mouse
+// release.
+bool MenuHost::ReleaseCaptureOnMouseReleased() {
+ return false;
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_host_win.h b/views/controls/menu/menu_host_win.h
new file mode 100644
index 0000000..49cc13b
--- /dev/null
+++ b/views/controls/menu/menu_host_win.h
@@ -0,0 +1,55 @@
+// 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.
+
+
+#ifndef VIEWS_CONTROLS_MENU_MENU_HOST_WIN_H_
+#define VIEWS_CONTROLS_MENU_MENU_HOST_WIN_H_
+
+#include "views/widget/widget_win.h"
+
+namespace views {
+
+class SubmenuView;
+
+// MenuHost implementation for windows.
+class MenuHost : public WidgetWin {
+ public:
+ explicit MenuHost(SubmenuView* submenu);
+
+ void Init(HWND parent,
+ const gfx::Rect& bounds,
+ View* contents_view,
+ bool do_capture);
+
+ void Show();
+ virtual void Hide();
+ virtual void HideWindow();
+ virtual void OnCaptureChanged(HWND hwnd);
+ void ReleaseCapture();
+
+ protected:
+ virtual RootView* CreateRootView();
+
+ virtual void OnCancelMode();
+
+ // Overriden to return false, we do NOT want to release capture on mouse
+ // release.
+ virtual bool ReleaseCaptureOnMouseReleased();
+
+ private:
+ // If true, we've been closed.
+ bool closed_;
+
+ // If true, we own the capture and need to release it.
+ bool owns_capture_;
+
+ // The view we contain.
+ SubmenuView* submenu_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuHost);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_MENU_HOST_WIN_H_
diff --git a/views/controls/menu/menu_item_view.cc b/views/controls/menu/menu_item_view.cc
index 69c57a2..6308b52 100644
--- a/views/controls/menu/menu_item_view.cc
+++ b/views/controls/menu/menu_item_view.cc
@@ -4,215 +4,18 @@
#include "views/controls/menu/menu_item_view.h"
-#if defined(OS_WIN)
-#include <windows.h>
-#include <uxtheme.h>
-#include <Vssym32.h>
-#endif
-
#include "app/gfx/canvas.h"
#include "app/l10n_util.h"
#include "grit/app_strings.h"
+#include "views/controls/menu/menu_config.h"
#include "views/controls/menu/menu_controller.h"
+#include "views/controls/menu/menu_separator.h"
#include "views/controls/menu/submenu_view.h"
-#if defined(OS_WIN)
-#include "app/l10n_util_win.h"
-#include "base/gfx/native_theme.h"
-#include "base/win_util.h"
-#endif
-
-// 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 used if the menu doesn't have icons.
-static const int kItemNoIconTopMargin = 1;
-static const int kItemNoIconBottomMargin = 3;
-
-// Margins between the left of the item and the icon.
-static const int kItemLeftMargin = 4;
-
-// Padding between the label and submenu arrow.
-static const int kLabelToArrowPadding = 10;
-
-// Padding between the arrow and the edge.
-static const int kArrowToEdgePadding = 5;
-
-// Padding between the icon and label.
-static const int kIconToLabelPadding = 8;
-
-// Padding between the gutter and label.
-static const int kGutterToLabel = 5;
-
-// Size of the check. This comes from the OS.
-static int check_width;
-static int check_height;
-
-// Size of the submenu arrow. This comes from the OS.
-static int arrow_width;
-static int arrow_height;
-
-// Width of the gutter. Only used if render_gutter is true.
-static int gutter_width;
-
-// Margins between the right of the item and the label.
-static int item_right_margin;
-
-// X-coordinate of where the label starts.
-static int label_start;
-
-// Height of the separator.
-static int separator_height;
-
-// Whether or not the gutter should be rendered. The gutter is specific to
-// Vista.
-static bool render_gutter = false;
-
-// Preferred height of menu items. Reset every time a menu is run.
-static int pref_menu_h;
-
-// Are mnemonics shown? This is updated before the menus are shown.
-static bool show_mnemonics;
-
-using gfx::NativeTheme;
-
namespace views {
namespace {
-// Returns the font menus are to use.
-gfx::Font GetMenuFont() {
-#if defined(OS_WIN)
- NONCLIENTMETRICS metrics;
- win_util::GetNonClientMetrics(&metrics);
-
- l10n_util::AdjustUIFont(&(metrics.lfMenuFont));
- HFONT font = CreateFontIndirect(&metrics.lfMenuFont);
- DLOG_ASSERT(font);
- return gfx::Font::CreateFont(font);
-#else
- return gfx::Font();
-#endif
-}
-
-// Calculates all sizes that we can from the OS.
-//
-// This is invoked prior to Running a menu.
-void UpdateMenuPartSizes(bool has_icons) {
-#if defined(OS_WIN)
- HDC dc = GetDC(NULL);
- RECT bounds = { 0, 0, 200, 200 };
- SIZE check_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, &bounds,
- TS_TRUE, &check_size) == S_OK) {
- check_width = check_size.cx;
- check_height = check_size.cy;
- } else {
- check_width = GetSystemMetrics(SM_CXMENUCHECK);
- check_height = GetSystemMetrics(SM_CYMENUCHECK);
- }
-
- SIZE arrow_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPSUBMENU, MSM_NORMAL, &bounds,
- TS_TRUE, &arrow_size) == S_OK) {
- arrow_width = arrow_size.cx;
- arrow_height = arrow_size.cy;
- } else {
- // Sadly I didn't see a specify metrics for this.
- arrow_width = GetSystemMetrics(SM_CXMENUCHECK);
- arrow_height = GetSystemMetrics(SM_CYMENUCHECK);
- }
-
- SIZE gutter_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPGUTTER, MSM_NORMAL, &bounds,
- TS_TRUE, &gutter_size) == S_OK) {
- gutter_width = gutter_size.cx;
- render_gutter = true;
- } else {
- gutter_width = 0;
- render_gutter = false;
- }
-
- SIZE separator_size;
- if (NativeTheme::instance()->GetThemePartSize(
- NativeTheme::MENU, dc, MENU_POPUPSEPARATOR, MSM_NORMAL, &bounds,
- TS_TRUE, &separator_size) == S_OK) {
- separator_height = separator_size.cy;
- } else {
- separator_height = GetSystemMetrics(SM_CYMENU) / 2;
- }
-
- item_right_margin = kLabelToArrowPadding + arrow_width + kArrowToEdgePadding;
-
- if (has_icons) {
- label_start = kItemLeftMargin + check_width + kIconToLabelPadding;
- } else {
- // If there are no icons don't pad by the icon to label padding. This
- // makes us look close to system menus.
- label_start = kItemLeftMargin + check_width;
- }
- if (render_gutter)
- label_start += gutter_width + kGutterToLabel;
-
- ReleaseDC(NULL, dc);
-
- MenuItemView menu_item(NULL);
- menu_item.SetTitle(L"blah"); // Text doesn't matter here.
- pref_menu_h = menu_item.GetPreferredSize().height();
-#endif
-}
-
-// Convenience for scrolling the view such that the origin is visible.
-static void ScrollToVisible(View* view) {
- view->ScrollRectToVisible(0, 0, view->width(), view->height());
-}
-
-// MenuSeparator ---------------------------------------------------------------
-
-// Renders a separator.
-
-class MenuSeparator : public View {
- public:
- MenuSeparator() {
- }
-
- void Paint(gfx::Canvas* canvas) {
- // The gutter is rendered before the background.
- int start_x = 0;
- int start_y = height() / 3;
- HDC dc = canvas->beginPlatformPaint();
- if (render_gutter) {
- // If render_gutter is true, we're on Vista and need to render the
- // gutter, then indent the separator from the gutter.
- RECT gutter_bounds = { label_start - kGutterToLabel - gutter_width, 0, 0,
- height() };
- gutter_bounds.right = gutter_bounds.left + gutter_width;
- NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER, MPI_NORMAL,
- &gutter_bounds);
- start_x = gutter_bounds.left + gutter_width;
- start_y = 0;
- }
- RECT separator_bounds = { start_x, start_y, width(), height() };
- NativeTheme::instance()->PaintMenuSeparator(dc, MENU_POPUPSEPARATOR,
- MPI_NORMAL, &separator_bounds);
- canvas->endPlatformPaint();
- }
-
- gfx::Size GetPreferredSize() {
- return gfx::Size(10, // Just in case we're the only item in a menu.
- separator_height);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MenuSeparator);
-};
-
// EmptyMenuMenuItem ---------------------------------------------------------
// EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem
@@ -247,6 +50,17 @@ const int MenuItemView::kEmptyMenuItemViewID =
// static
bool MenuItemView::allow_task_nesting_during_run_ = false;
+// static
+int MenuItemView::label_start_;
+
+// Margins between the right of the item and the label.
+// static
+int MenuItemView::item_right_margin_;
+
+// Preferred height of menu items. Reset every time a menu is run.
+// static
+int MenuItemView::pref_menu_height_;
+
MenuItemView::MenuItemView(MenuDelegate* delegate) {
// NOTE: don't check the delegate for NULL, UpdateMenuPartSizes supplies a
// NULL delegate.
@@ -270,12 +84,7 @@ MenuItemView::~MenuItemView() {
delete submenu_;
}
-// static
-int MenuItemView::pref_menu_height() {
- return pref_menu_h;
-}
-
-void MenuItemView::RunMenuAt(HWND parent,
+void MenuItemView::RunMenuAt(gfx::NativeView parent,
const gfx::Rect& bounds,
AnchorPosition anchor,
bool has_mnemonics) {
@@ -327,7 +136,7 @@ void MenuItemView::RunMenuAt(HWND parent,
delegate_->ExecuteCommand(result->GetCommand(), mouse_event_flags);
}
-void MenuItemView::RunMenuForDropAt(HWND parent,
+void MenuItemView::RunMenuForDropAt(gfx::NativeView parent,
const gfx::Rect& bounds,
AnchorPosition anchor) {
PrepareForRun(false);
@@ -381,9 +190,9 @@ void MenuItemView::Paint(gfx::Canvas* canvas) {
}
gfx::Size MenuItemView::GetPreferredSize() {
- gfx::Font& font = GetRootMenuItem()->font_;
+ const gfx::Font& font = MenuConfig::instance().font;
return gfx::Size(
- font.GetStringWidth(title_) + label_start + item_right_margin,
+ font.GetStringWidth(title_) + label_start_ + item_right_margin_,
font.height() + GetBottomMargin() + GetTopMargin());
}
@@ -430,6 +239,32 @@ MenuItemView::MenuItemView(MenuItemView* parent,
Init(parent, command, type, NULL);
}
+// Calculates all sizes that we can from the OS.
+//
+// This is invoked prior to Running a menu.
+void MenuItemView::UpdateMenuPartSizes(bool has_icons) {
+ MenuConfig::Reset();
+ const MenuConfig& config = MenuConfig::instance();
+
+ item_right_margin_ = config.label_to_arrow_padding + config.arrow_width +
+ config.arrow_to_edge_padding;
+
+ if (has_icons) {
+ label_start_ = config.item_left_margin + config.check_width +
+ config.icon_to_label_padding;
+ } else {
+ // If there are no icons don't pad by the icon to label padding. This
+ // makes us look close to system menus.
+ label_start_ = config.item_left_margin + config.check_width;
+ }
+ if (config.render_gutter)
+ label_start_ += config.gutter_width + config.gutter_to_label;
+
+ MenuItemView menu_item(NULL);
+ menu_item.SetTitle(L"blah"); // Text doesn't matter here.
+ pref_menu_height_ = menu_item.GetPreferredSize().height();
+}
+
void MenuItemView::Init(MenuItemView* parent,
int command,
MenuItemView::Type type,
@@ -528,13 +363,6 @@ void MenuItemView::PrepareForRun(bool has_mnemonics) {
// things may shift around.
UpdateMenuPartSizes(has_icons_);
}
-
- font_ = GetMenuFont();
-
- BOOL show_cues;
- show_mnemonics =
- (SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &show_cues, 0) &&
- show_cues == TRUE);
}
int MenuItemView::GetDrawStringFlags() {
@@ -545,7 +373,7 @@ int MenuItemView::GetDrawStringFlags() {
flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
if (has_mnemonics_) {
- if (show_mnemonics)
+ if (MenuConfig::instance().show_mnemonics)
flags |= gfx::Canvas::SHOW_PREFIX;
else
flags |= gfx::Canvas::HIDE_PREFIX;
@@ -587,122 +415,6 @@ void MenuItemView::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
rect->set_x(MirroredLeftPointForRect(*rect));
}
-void MenuItemView::Paint(gfx::Canvas* canvas, bool for_drag) {
- bool render_selection =
- (!for_drag && IsSelected() &&
- parent_menu_item_->GetSubmenu()->GetShowSelection(this));
- int state = render_selection ? MPI_HOT :
- (IsEnabled() ? MPI_NORMAL : MPI_DISABLED);
- HDC dc = canvas->beginPlatformPaint();
-
- // The gutter is rendered before the background.
- if (render_gutter && !for_drag) {
- gfx::Rect gutter_bounds(label_start - kGutterToLabel - gutter_width, 0,
- gutter_width, height());
- AdjustBoundsForRTLUI(&gutter_bounds);
- RECT gutter_rect = gutter_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER, MPI_NORMAL,
- &gutter_rect);
- }
-
- // Render the background.
- if (!for_drag) {
- gfx::Rect item_bounds(0, 0, width(), height());
- AdjustBoundsForRTLUI(&item_bounds);
- RECT item_rect = item_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuItemBackground(
- NativeTheme::MENU, dc, MENU_POPUPITEM, state, render_selection,
- &item_rect);
- }
-
- int icon_x = kItemLeftMargin;
- int top_margin = GetTopMargin();
- int bottom_margin = GetBottomMargin();
- int icon_y = top_margin + (height() - kItemTopMargin -
- bottom_margin - check_height) / 2;
- int icon_height = check_height;
- int icon_width = check_width;
-
- if (type_ == CHECKBOX && GetDelegate()->IsItemChecked(GetCommand())) {
- // Draw the check background.
- gfx::Rect check_bg_bounds(0, 0, icon_x + icon_width, height());
- const int bg_state = IsEnabled() ? MCB_NORMAL : MCB_DISABLED;
- AdjustBoundsForRTLUI(&check_bg_bounds);
- RECT check_bg_rect = check_bg_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuCheckBackground(
- NativeTheme::MENU, dc, MENU_POPUPCHECKBACKGROUND, bg_state,
- &check_bg_rect);
-
- // And the check.
- gfx::Rect check_bounds(icon_x, icon_y, icon_width, icon_height);
- const int check_state = IsEnabled() ? MC_CHECKMARKNORMAL :
- MC_CHECKMARKDISABLED;
- AdjustBoundsForRTLUI(&check_bounds);
- RECT check_rect = check_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuCheck(
- NativeTheme::MENU, dc, MENU_POPUPCHECK, check_state, &check_rect,
- render_selection);
- }
-
- // Render the foreground.
- // Menu color is specific to Vista, fallback to classic colors if can't
- // get color.
- int default_sys_color = render_selection ? COLOR_HIGHLIGHTTEXT :
- (IsEnabled() ? COLOR_MENUTEXT : COLOR_GRAYTEXT);
- SkColor fg_color = NativeTheme::instance()->GetThemeColorWithDefault(
- NativeTheme::MENU, MENU_POPUPITEM, state, TMT_TEXTCOLOR,
- default_sys_color);
- int width = this->width() - item_right_margin - label_start;
- gfx::Font& font = GetRootMenuItem()->font_;
- gfx::Rect text_bounds(label_start, top_margin, width, font.height());
- text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
- if (for_drag) {
- // With different themes, it's difficult to tell what the correct foreground
- // and background colors are for the text to draw the correct halo. Instead,
- // just draw black on white, which will look good in most cases.
- canvas->DrawStringWithHalo(GetTitle(), font, 0x00000000, 0xFFFFFFFF,
- text_bounds.x(), text_bounds.y(),
- text_bounds.width(), text_bounds.height(),
- GetRootMenuItem()->GetDrawStringFlags());
- } else {
- canvas->DrawStringInt(GetTitle(), font, fg_color,
- text_bounds.x(), text_bounds.y(), text_bounds.width(),
- text_bounds.height(),
- GetRootMenuItem()->GetDrawStringFlags());
- }
-
- if (icon_.width() > 0) {
- gfx::Rect icon_bounds(kItemLeftMargin,
- top_margin + (height() - top_margin -
- bottom_margin - icon_.height()) / 2,
- icon_.width(),
- icon_.height());
- icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
- canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
- }
-
- if (HasSubmenu()) {
- int state_id = IsEnabled() ? MSM_NORMAL : MSM_DISABLED;
- gfx::Rect arrow_bounds(this->width() - item_right_margin + kLabelToArrowPadding,
- 0, arrow_width, height());
- AdjustBoundsForRTLUI(&arrow_bounds);
-
- // If our sub menus open from right to left (which is the case when the
- // locale is RTL) then we should make sure the menu arrow points to the
- // right direction.
- NativeTheme::MenuArrowDirection arrow_direction;
- if (UILayoutIsRightToLeft())
- arrow_direction = NativeTheme::LEFT_POINTING_ARROW;
- else
- arrow_direction = NativeTheme::RIGHT_POINTING_ARROW;
-
- RECT arrow_rect = arrow_bounds.ToRECT();
- NativeTheme::instance()->PaintMenuArrow(
- NativeTheme::MENU, dc, MENU_POPUPSUBMENU, state_id, &arrow_rect,
- arrow_direction, render_selection);
- }
- canvas->endPlatformPaint();
-}
void MenuItemView::DestroyAllMenuHosts() {
if (!HasSubmenu())
@@ -717,13 +429,16 @@ void MenuItemView::DestroyAllMenuHosts() {
int MenuItemView::GetTopMargin() {
MenuItemView* root = GetRootMenuItem();
- return root && root->has_icons_ ? kItemTopMargin : kItemNoIconTopMargin;
+ return root && root->has_icons_
+ ? MenuConfig::instance().item_top_margin :
+ MenuConfig::instance().item_no_icon_top_margin;
}
int MenuItemView::GetBottomMargin() {
MenuItemView* root = GetRootMenuItem();
- return root && root->has_icons_ ?
- kItemBottomMargin : kItemNoIconBottomMargin;
+ return root && root->has_icons_
+ ? MenuConfig::instance().item_bottom_margin :
+ MenuConfig::instance().item_no_icon_bottom_margin;
}
} // namespace views
diff --git a/views/controls/menu/menu_item_view.h b/views/controls/menu/menu_item_view.h
index b15bfa3..86b1bbf 100644
--- a/views/controls/menu/menu_item_view.h
+++ b/views/controls/menu/menu_item_view.h
@@ -5,7 +5,6 @@
#ifndef VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
#define VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
-#include "app/gfx/font.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/view.h"
@@ -73,7 +72,10 @@ class MenuItemView : public View {
// Returns the preferred height of menu items. This is only valid when the
// menu is about to be shown.
- static int pref_menu_height();
+ static int pref_menu_height() { return pref_menu_height_; }
+
+ // X-coordinate of where the label starts.
+ static int label_start() { return label_start_; }
// Run methods. See description above class for details. Both Run methods take
// a rectangle, which is used to position the menu. |has_mnemonics| indicates
@@ -159,9 +161,6 @@ class MenuItemView : public View {
// Returns the parent menu item.
MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }
- // Sets the font.
- void SetFont(const gfx::Font& font) { font_ = font; }
-
// Sets the title
void SetTitle(const std::wstring& title) {
title_ = title;
@@ -222,6 +221,11 @@ class MenuItemView : public View {
MenuItemView(MenuItemView* parent, int command, Type type);
private:
+ // Calculates all sizes that we can from the OS.
+ //
+ // This is invoked prior to Running a menu.
+ static void UpdateMenuPartSizes(bool has_icons);
+
// Called by the two constructors to initialize this menu item.
void Init(MenuItemView* parent,
int command,
@@ -300,9 +304,6 @@ class MenuItemView : public View {
// Submenu, created via CreateSubmenu.
SubmenuView* submenu_;
- // Font.
- gfx::Font font_;
-
// Title.
std::wstring title_;
@@ -314,6 +315,15 @@ class MenuItemView : public View {
bool has_icons_;
+ // X-coordinate of where the label starts.
+ static int label_start_;
+
+ // Margins between the right of the item and the label.
+ static int item_right_margin_;
+
+ // Preferred height of menu items. Reset every time a menu is run.
+ static int pref_menu_height_;
+
DISALLOW_COPY_AND_ASSIGN(MenuItemView);
};
diff --git a/views/controls/menu/menu_item_view_gtk.cc b/views/controls/menu/menu_item_view_gtk.cc
new file mode 100644
index 0000000..6f5b8e4
--- /dev/null
+++ b/views/controls/menu/menu_item_view_gtk.cc
@@ -0,0 +1,16 @@
+// 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/menu_item_view.h"
+
+#include "base/logging.h"
+#include "views/controls/menu/menu_config.h"
+
+namespace views {
+
+void MenuItemView::Paint(gfx::Canvas* canvas, bool for_drag) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_item_view_win.cc b/views/controls/menu/menu_item_view_win.cc
new file mode 100644
index 0000000..8f3812c
--- /dev/null
+++ b/views/controls/menu/menu_item_view_win.cc
@@ -0,0 +1,143 @@
+// 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/menu_item_view.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <Vssym32.h>
+
+#include "app/gfx/canvas.h"
+#include "base/gfx/native_theme.h"
+#include "app/l10n_util.h"
+#include "grit/app_strings.h"
+#include "views/controls/menu/menu_config.h"
+#include "views/controls/menu/submenu_view.h"
+
+using gfx::NativeTheme;
+
+namespace views {
+
+void MenuItemView::Paint(gfx::Canvas* canvas, bool for_drag) {
+ const MenuConfig& config = MenuConfig::instance();
+ bool render_selection =
+ (!for_drag && IsSelected() &&
+ parent_menu_item_->GetSubmenu()->GetShowSelection(this));
+ int state = render_selection ? MPI_HOT :
+ (IsEnabled() ? MPI_NORMAL : MPI_DISABLED);
+ HDC dc = canvas->beginPlatformPaint();
+
+ // The gutter is rendered before the background.
+ if (config.render_gutter && !for_drag) {
+ gfx::Rect gutter_bounds(label_start_ - config.gutter_to_label -
+ config.gutter_width, 0, config.gutter_width,
+ height());
+ AdjustBoundsForRTLUI(&gutter_bounds);
+ RECT gutter_rect = gutter_bounds.ToRECT();
+ NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER, MPI_NORMAL,
+ &gutter_rect);
+ }
+
+ // Render the background.
+ if (!for_drag) {
+ gfx::Rect item_bounds(0, 0, width(), height());
+ AdjustBoundsForRTLUI(&item_bounds);
+ RECT item_rect = item_bounds.ToRECT();
+ NativeTheme::instance()->PaintMenuItemBackground(
+ NativeTheme::MENU, dc, MENU_POPUPITEM, state, render_selection,
+ &item_rect);
+ }
+
+ int icon_x = config.item_left_margin;
+ int top_margin = GetTopMargin();
+ int bottom_margin = GetBottomMargin();
+ int icon_y = top_margin + (height() - config.item_top_margin -
+ bottom_margin - config.check_height) / 2;
+ int icon_height = config.check_height;
+ int icon_width = config.check_width;
+
+ if (type_ == CHECKBOX && GetDelegate()->IsItemChecked(GetCommand())) {
+ // Draw the check background.
+ gfx::Rect check_bg_bounds(0, 0, icon_x + icon_width, height());
+ const int bg_state = IsEnabled() ? MCB_NORMAL : MCB_DISABLED;
+ AdjustBoundsForRTLUI(&check_bg_bounds);
+ RECT check_bg_rect = check_bg_bounds.ToRECT();
+ NativeTheme::instance()->PaintMenuCheckBackground(
+ NativeTheme::MENU, dc, MENU_POPUPCHECKBACKGROUND, bg_state,
+ &check_bg_rect);
+
+ // And the check.
+ gfx::Rect check_bounds(icon_x, icon_y, icon_width, icon_height);
+ const int check_state = IsEnabled() ? MC_CHECKMARKNORMAL :
+ MC_CHECKMARKDISABLED;
+ AdjustBoundsForRTLUI(&check_bounds);
+ RECT check_rect = check_bounds.ToRECT();
+ NativeTheme::instance()->PaintMenuCheck(
+ NativeTheme::MENU, dc, MENU_POPUPCHECK, check_state, &check_rect,
+ render_selection);
+ }
+
+ // Render the foreground.
+ // Menu color is specific to Vista, fallback to classic colors if can't
+ // get color.
+ int default_sys_color = render_selection ? COLOR_HIGHLIGHTTEXT :
+ (IsEnabled() ? COLOR_MENUTEXT : COLOR_GRAYTEXT);
+ SkColor fg_color = NativeTheme::instance()->GetThemeColorWithDefault(
+ NativeTheme::MENU, MENU_POPUPITEM, state, TMT_TEXTCOLOR,
+ default_sys_color);
+ int width = this->width() - item_right_margin_ - label_start_;
+ const gfx::Font& font = MenuConfig::instance().font;
+ gfx::Rect text_bounds(label_start_, top_margin, width, font.height());
+ text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
+ if (for_drag) {
+ // With different themes, it's difficult to tell what the correct
+ // foreground and background colors are for the text to draw the correct
+ // halo. Instead, just draw black on white, which will look good in most
+ // cases.
+ canvas->DrawStringWithHalo(GetTitle(), font, 0x00000000, 0xFFFFFFFF,
+ text_bounds.x(), text_bounds.y(),
+ text_bounds.width(), text_bounds.height(),
+ GetRootMenuItem()->GetDrawStringFlags());
+ } else {
+ canvas->DrawStringInt(GetTitle(), font, fg_color,
+ text_bounds.x(), text_bounds.y(), text_bounds.width(),
+ text_bounds.height(),
+ GetRootMenuItem()->GetDrawStringFlags());
+ }
+
+ if (icon_.width() > 0) {
+ gfx::Rect icon_bounds(config.item_left_margin,
+ top_margin + (height() - top_margin -
+ bottom_margin - icon_.height()) / 2,
+ icon_.width(),
+ icon_.height());
+ icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
+ canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
+ }
+
+ if (HasSubmenu()) {
+ int state_id = IsEnabled() ? MSM_NORMAL : MSM_DISABLED;
+ gfx::Rect arrow_bounds(this->width() - item_right_margin_ +
+ config.label_to_arrow_padding, 0,
+ config.arrow_width, height());
+ AdjustBoundsForRTLUI(&arrow_bounds);
+
+ // If our sub menus open from right to left (which is the case when the
+ // locale is RTL) then we should make sure the menu arrow points to the
+ // right direction.
+ NativeTheme::MenuArrowDirection arrow_direction;
+ if (UILayoutIsRightToLeft())
+ arrow_direction = NativeTheme::LEFT_POINTING_ARROW;
+ else
+ arrow_direction = NativeTheme::RIGHT_POINTING_ARROW;
+
+ RECT arrow_rect = arrow_bounds.ToRECT();
+ NativeTheme::instance()->PaintMenuArrow(
+ NativeTheme::MENU, dc, MENU_POPUPSUBMENU, state_id, &arrow_rect,
+ arrow_direction, render_selection);
+ }
+ canvas->endPlatformPaint();
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_scroll_view_container.cc b/views/controls/menu/menu_scroll_view_container.cc
index bd233d9..45a4777 100644
--- a/views/controls/menu/menu_scroll_view_container.cc
+++ b/views/controls/menu/menu_scroll_view_container.cc
@@ -12,17 +12,23 @@
#include "app/gfx/canvas.h"
#include "app/gfx/color_utils.h"
-#include "base/gfx/native_theme.h"
#include "views/border.h"
+#include "views/controls/menu/menu_config.h"
#include "views/controls/menu/menu_controller.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/submenu_view.h"
+#if defined(OS_WIN)
+#include "base/gfx/native_theme.h"
+#endif
+
+#if defined(OS_WIN)
using gfx::NativeTheme;
+#endif
// Height of the scroll arrow.
// This goes up to 4 with large fonts, but this is close enough for now.
-static const int kScrollArrowHeight = 3;
+static const int scroll_arrow_height = 3;
namespace views {
@@ -44,7 +50,8 @@ class MenuScrollButton : public View {
}
virtual gfx::Size GetPreferredSize() {
- return gfx::Size(kScrollArrowHeight * 2 - 1, pref_height_);
+ return gfx::Size(MenuConfig::instance().scroll_arrow_height * 2 - 1,
+ pref_height_);
}
virtual bool CanDrop(const OSExchangeData& data) {
@@ -72,6 +79,8 @@ class MenuScrollButton : public View {
}
virtual void Paint(gfx::Canvas* canvas) {
+#if defined(OS_WIN)
+ const MenuConfig& config = MenuConfig::instance();
HDC dc = canvas->beginPlatformPaint();
// The background.
@@ -82,17 +91,20 @@ class MenuScrollButton : public View {
// Then the arrow.
int x = width() / 2;
- int y = (height() - kScrollArrowHeight) / 2;
+ int y = (height() - config.scroll_arrow_height) / 2;
int delta_y = 1;
if (!is_up_) {
delta_y = -1;
- y += kScrollArrowHeight;
+ y += config.scroll_arrow_height;
}
SkColor arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT);
- for (int i = 0; i < kScrollArrowHeight; ++i, --x, y += delta_y)
+ for (int i = 0; i < config.scroll_arrow_height; ++i, --x, y += delta_y)
canvas->FillRectInt(arrow_color, x, y, (i * 2) + 1, 1);
canvas->endPlatformPaint();
+#else
+ NOTIMPLEMENTED();
+#endif
}
private:
@@ -166,11 +178,15 @@ MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view) {
}
void MenuScrollViewContainer::Paint(gfx::Canvas* canvas) {
+#if defined(OS_WIN)
HDC dc = canvas->beginPlatformPaint();
RECT bounds = {0, 0, width(), height()};
NativeTheme::instance()->PaintMenuBackground(
NativeTheme::MENU, dc, MENU_POPUPBACKGROUND, 0, &bounds);
canvas->endPlatformPaint();
+#else
+ NOTIMPLEMENTED();
+#endif
}
void MenuScrollViewContainer::Layout() {
diff --git a/views/controls/menu/menu_separator.h b/views/controls/menu/menu_separator.h
new file mode 100644
index 0000000..af37f8a
--- /dev/null
+++ b/views/controls/menu/menu_separator.h
@@ -0,0 +1,27 @@
+// 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.
+
+
+#ifndef VIEWS_CONTROLS_MENU_MENU_SEPARATOR_H_
+#define VIEWS_CONTROLS_MENU_MENU_SEPARATOR_H_
+
+#include "views/view.h"
+
+namespace views {
+
+class MenuSeparator : public View {
+ public:
+ MenuSeparator() {}
+
+ // View overrides.
+ void Paint(gfx::Canvas* canvas);
+ gfx::Size GetPreferredSize();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MenuSeparator);
+};
+
+} // namespace views
+
+#endif // VIEWS_CONTROLS_MENU_MENU_SEPARATOR_H_
diff --git a/views/controls/menu/menu_separator_gtk.cc b/views/controls/menu/menu_separator_gtk.cc
new file mode 100644
index 0000000..fdb5e8f
--- /dev/null
+++ b/views/controls/menu/menu_separator_gtk.cc
@@ -0,0 +1,21 @@
+// 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_separator.h"
+
+#include "base/logging.h"
+#include "views/controls/menu/menu_config.h"
+
+namespace views {
+
+void MenuSeparator::Paint(gfx::Canvas* canvas) {
+ NOTIMPLEMENTED();
+}
+
+gfx::Size MenuSeparator::GetPreferredSize() {
+ return gfx::Size(10, // Just in case we're the only item in a menu.
+ MenuConfig::instance().separator_height);
+}
+
+} // namespace views
diff --git a/views/controls/menu/menu_separator_win.cc b/views/controls/menu/menu_separator_win.cc
new file mode 100644
index 0000000..b2ffe5e2
--- /dev/null
+++ b/views/controls/menu/menu_separator_win.cc
@@ -0,0 +1,47 @@
+// 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_separator.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <Vssym32.h>
+
+#include "app/gfx/canvas.h"
+#include "base/gfx/native_theme.h"
+#include "views/controls/menu/menu_config.h"
+#include "views/controls/menu/menu_item_view.h"
+
+namespace views {
+
+void MenuSeparator::Paint(gfx::Canvas* canvas) {
+ const MenuConfig& config = MenuConfig::instance();
+ // The gutter is rendered before the background.
+ int start_x = 0;
+ int start_y = height() / 3;
+ HDC dc = canvas->beginPlatformPaint();
+ if (config.render_gutter) {
+ // If render_gutter is true, we're on Vista and need to render the
+ // gutter, then indent the separator from the gutter.
+ RECT gutter_bounds = { MenuItemView::label_start() -
+ config.gutter_to_label - config.gutter_width, 0, 0,
+ height() };
+ gutter_bounds.right = gutter_bounds.left + config.gutter_width;
+ gfx::NativeTheme::instance()->PaintMenuGutter(dc, MENU_POPUPGUTTER,
+ MPI_NORMAL, &gutter_bounds);
+ start_x = gutter_bounds.left + config.gutter_width;
+ start_y = 0;
+ }
+ RECT separator_bounds = { start_x, start_y, width(), height() };
+ gfx::NativeTheme::instance()->PaintMenuSeparator(
+ dc, MENU_POPUPSEPARATOR, MPI_NORMAL, &separator_bounds);
+ canvas->endPlatformPaint();
+}
+
+gfx::Size MenuSeparator::GetPreferredSize() {
+ return gfx::Size(10, // Just in case we're the only item in a menu.
+ MenuConfig::instance().separator_height);
+}
+
+} // namespace views
diff --git a/views/controls/menu/submenu_view.cc b/views/controls/menu/submenu_view.cc
index ddf47c9..46580c0 100644
--- a/views/controls/menu/submenu_view.cc
+++ b/views/controls/menu/submenu_view.cc
@@ -10,8 +10,9 @@
#include "views/widget/root_view.h"
#if defined(OS_WIN)
-#include "base/win_util.h"
-#include "views/widget/widget_win.h"
+#include "views/controls/menu/menu_host_win.h"
+#elif defined(OS_LINUX)
+#include "views/controls/menu/menu_host_gtk.h"
#endif
// Height of the drop indicator. This should be an even number.
@@ -22,236 +23,6 @@ static const SkColor kDropIndicatorColor = SK_ColorBLACK;
namespace views {
-// MenuHostRootView ----------------------------------------------------------
-
-// MenuHostRootView is the RootView of the window showing the menu.
-// SubmenuView's scroll view is added as a child of MenuHostRootView.
-// MenuHostRootView forwards relevant events to the MenuController.
-//
-// As all the menu items are owned by the root menu item, care must be taken
-// such that when MenuHostRootView is deleted it doesn't delete the menu items.
-class MenuHostRootView : public RootView {
- public:
- explicit MenuHostRootView(Widget* widget,
- SubmenuView* submenu)
- : RootView(widget),
- submenu_(submenu),
- forward_drag_to_menu_controller_(true),
- suspend_events_(false) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << " new MenuHostRootView " << this;
-#endif
- }
-
- virtual bool OnMousePressed(const MouseEvent& event) {
- if (suspend_events_)
- return true;
-
- forward_drag_to_menu_controller_ =
- ((event.x() < 0 || event.y() < 0 || event.x() >= width() ||
- event.y() >= height()) ||
- !RootView::OnMousePressed(event));
- if (forward_drag_to_menu_controller_)
- GetMenuController()->OnMousePressed(submenu_, event);
- return true;
- }
-
- virtual bool OnMouseDragged(const MouseEvent& event) {
- if (suspend_events_)
- return true;
-
- if (forward_drag_to_menu_controller_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << " MenuHostRootView::OnMouseDragged source=" << submenu_;
-#endif
- GetMenuController()->OnMouseDragged(submenu_, event);
- return true;
- }
- return RootView::OnMouseDragged(event);
- }
-
- virtual void OnMouseReleased(const MouseEvent& event, bool canceled) {
- if (suspend_events_)
- return;
-
- RootView::OnMouseReleased(event, canceled);
- if (forward_drag_to_menu_controller_) {
- forward_drag_to_menu_controller_ = false;
- if (canceled) {
- GetMenuController()->Cancel(true);
- } else {
- GetMenuController()->OnMouseReleased(submenu_, event);
- }
- }
- }
-
- virtual void OnMouseMoved(const MouseEvent& event) {
- if (suspend_events_)
- return;
-
- RootView::OnMouseMoved(event);
- GetMenuController()->OnMouseMoved(submenu_, event);
- }
-
- virtual void ProcessOnMouseExited() {
- if (suspend_events_)
- return;
-
- RootView::ProcessOnMouseExited();
- }
-
- virtual bool ProcessMouseWheelEvent(const MouseWheelEvent& e) {
- // RootView::ProcessMouseWheelEvent forwards to the focused view. We don't
- // have a focused view, so we need to override this then forward to
- // the menu.
- return submenu_->OnMouseWheel(e);
- }
-
- void SuspendEvents() {
- suspend_events_ = true;
- }
-
- private:
- MenuController* GetMenuController() {
- return submenu_->GetMenuItem()->GetMenuController();
- }
-
- // The SubmenuView we contain.
- SubmenuView* submenu_;
-
- // Whether mouse dragged/released should be forwarded to the MenuController.
- bool forward_drag_to_menu_controller_;
-
- // Whether events are suspended. If true, no events are forwarded to the
- // MenuController.
- bool suspend_events_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuHostRootView);
-};
-
-// MenuHost ------------------------------------------------------------------
-
-// MenuHost is the window responsible for showing a single menu.
-//
-// Similar to MenuHostRootView, care must be taken such that when MenuHost is
-// deleted, it doesn't delete the menu items. MenuHost is closed via a
-// DelayedClosed, which avoids timing issues with deleting the window while
-// capture or events are directed at it.
-
-class MenuHost : public WidgetWin {
- public:
- explicit MenuHost(SubmenuView* submenu)
- : closed_(false),
- submenu_(submenu),
- owns_capture_(false) {
- set_window_style(WS_POPUP);
- set_initial_class_style(
- (win_util::GetWinVersion() < win_util::WINVERSION_XP) ?
- 0 : CS_DROPSHADOW);
- is_mouse_down_ =
- ((GetKeyState(VK_LBUTTON) & 0x80) ||
- (GetKeyState(VK_RBUTTON) & 0x80) ||
- (GetKeyState(VK_MBUTTON) & 0x80) ||
- (GetKeyState(VK_XBUTTON1) & 0x80) ||
- (GetKeyState(VK_XBUTTON2) & 0x80));
- // Mouse clicks shouldn't give us focus.
- set_window_ex_style(WS_EX_TOPMOST | WS_EX_NOACTIVATE);
- }
-
- void Init(HWND parent,
- const gfx::Rect& bounds,
- View* contents_view,
- bool do_capture) {
- WidgetWin::Init(parent, bounds);
- SetContentsView(contents_view);
- // We don't want to take focus away from the hosting window.
- ShowWindow(SW_SHOWNA);
- owns_capture_ = do_capture;
- if (do_capture) {
- SetCapture();
- has_capture_ = true;
-#ifdef DEBUG_MENU
- DLOG(INFO) << "Doing capture";
-#endif
- }
- }
-
- virtual void Hide() {
- if (closed_) {
- // We're already closed, nothing to do.
- // This is invoked twice if the first time just hid us, and the second
- // time deleted Closed (deleted) us.
- return;
- }
- // The menus are freed separately, and possibly before the window is closed,
- // remove them so that View doesn't try to access deleted objects.
- static_cast<MenuHostRootView*>(GetRootView())->SuspendEvents();
- GetRootView()->RemoveAllChildViews(false);
- closed_ = true;
- ReleaseCapture();
- WidgetWin::Hide();
- }
-
- virtual void HideWindow() {
- // Make sure we release capture before hiding.
- ReleaseCapture();
- WidgetWin::Hide();
- }
-
- virtual void OnCaptureChanged(HWND hwnd) {
- WidgetWin::OnCaptureChanged(hwnd);
- owns_capture_ = false;
-#ifdef DEBUG_MENU
- DLOG(INFO) << "Capture changed";
-#endif
- }
-
- void ReleaseCapture() {
- if (owns_capture_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "released capture";
-#endif
- owns_capture_ = false;
- ::ReleaseCapture();
- }
- }
-
- protected:
- // Overriden to create MenuHostRootView.
- virtual RootView* CreateRootView() {
- return new MenuHostRootView(this, submenu_);
- }
-
- virtual void OnCancelMode() {
- if (!closed_) {
-#ifdef DEBUG_MENU
- DLOG(INFO) << "OnCanceMode, closing menu";
-#endif
- submenu_->GetMenuItem()->GetMenuController()->Cancel(true);
- }
- }
-
- // Overriden to return false, we do NOT want to release capture on mouse
- // release.
- virtual bool ReleaseCaptureOnMouseReleased() {
- return false;
- }
-
- private:
- // If true, we've been closed.
- bool closed_;
-
- // If true, we own the capture and need to release it.
- bool owns_capture_;
-
- // The view we contain.
- SubmenuView* submenu_;
-
- DISALLOW_COPY_AND_ASSIGN(MenuHost);
-};
-
-// SubmenuView ---------------------------------------------------------------
-
// static
const int SubmenuView::kSubmenuBorderSize = 3;
@@ -394,7 +165,11 @@ bool SubmenuView::OnMouseWheel(const MouseWheelEvent& e) {
// If the first item isn't entirely visible, make it visible, otherwise make
// the next/previous one entirely visible.
+#if defined(OS_WIN)
int delta = abs(e.GetOffset() / WHEEL_DELTA);
+#elif defined(OS_LINUX)
+ int delta = abs(e.GetOffset());
+#endif
bool scroll_up = (e.GetOffset() > 0);
while (delta-- > 0) {
int scroll_amount = 0;
@@ -430,11 +205,11 @@ bool SubmenuView::IsShowing() {
return host_ && host_->IsVisible();
}
-void SubmenuView::ShowAt(HWND parent,
+void SubmenuView::ShowAt(gfx::NativeView parent,
const gfx::Rect& bounds,
bool do_capture) {
if (host_) {
- host_->ShowWindow(SW_SHOWNA);
+ host_->Show();
return;
}