summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-25 03:59:54 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-25 03:59:54 +0000
commit2c6ba36933e03b2ce7dfed10f5a8ca6f77f46a2d (patch)
tree3a76761c951a272de703d260375af0db53f2bb95
parent1d39ec059f33a99601c724b1d8e8f4528b01d049 (diff)
downloadchromium_src-2c6ba36933e03b2ce7dfed10f5a8ca6f77f46a2d.zip
chromium_src-2c6ba36933e03b2ce7dfed10f5a8ca6f77f46a2d.tar.gz
chromium_src-2c6ba36933e03b2ce7dfed10f5a8ca6f77f46a2d.tar.bz2
Moves ownership of MenuItemView to MenuRunner as well as responbility
for showing (running) the menu entirely to MenuRunner. This way I can guarantee if MenuRunner is deleted, the related menu classes aren't deleted until the nested message loop completes. BUG=57890 TEST=thorougly test all menus in chrome: bookmarks, wrench menu, context menu on page, extension menus... R=ben@chromium.org Review URL: http://codereview.chromium.org/7720012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98179 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/frame/browser_view.cc24
-rw-r--r--chrome/browser/chromeos/frame/browser_view.h3
-rw-r--r--chrome/browser/chromeos/login/language_switch_menu.cc10
-rw-r--r--chrome/browser/chromeos/login/language_switch_menu.h8
-rw-r--r--chrome/browser/chromeos/notifications/balloon_view.cc15
-rw-r--r--chrome/browser/chromeos/status/clock_menu_button.cc49
-rw-r--r--chrome/browser/chromeos/status/clock_menu_button.h6
-rw-r--r--chrome/browser/chromeos/status/input_method_menu.cc11
-rw-r--r--chrome/browser/chromeos/status/input_method_menu.h4
-rw-r--r--chrome/browser/chromeos/status/memory_menu_button.cc42
-rw-r--r--chrome/browser/chromeos/status/memory_menu_button.h9
-rw-r--r--chrome/browser/chromeos/status/network_menu.cc17
-rw-r--r--chrome/browser/chromeos/status/network_menu.h4
-rw-r--r--chrome/browser/chromeos/status/power_menu_button.cc36
-rw-r--r--chrome/browser/chromeos/status/power_menu_button.h8
-rw-r--r--chrome/browser/ui/panels/panel_browser_frame_view.cc13
-rw-r--r--chrome/browser/ui/panels/panel_browser_frame_view.h5
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_context_menu.cc12
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_context_menu.h10
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc26
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h8
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc108
-rw-r--r--chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h7
-rw-r--r--chrome/browser/ui/views/browser_actions_container.cc12
-rw-r--r--chrome/browser/ui/views/compact_nav/compact_options_bar.cc2
-rw-r--r--chrome/browser/ui/views/compact_nav/compact_options_bar.h2
-rw-r--r--chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.cc23
-rw-r--r--chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h8
-rw-r--r--chrome/browser/ui/views/infobars/after_translate_infobar.cc11
-rw-r--r--chrome/browser/ui/views/infobars/before_translate_infobar.cc10
-rw-r--r--chrome/browser/ui/views/infobars/extension_infobar.cc11
-rw-r--r--chrome/browser/ui/views/location_bar/page_action_image_view.cc10
-rw-r--r--chrome/browser/ui/views/menu_item_view_test.cc44
-rw-r--r--chrome/browser/ui/views/menu_model_adapter_test.cc26
-rw-r--r--chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc8
-rw-r--r--chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc32
-rw-r--r--chrome/browser/ui/views/toolbar_view.cc2
-rw-r--r--chrome/browser/ui/views/toolbar_view.h3
-rw-r--r--chrome/browser/ui/views/wrench_menu.cc33
-rw-r--r--chrome/browser/ui/views/wrench_menu.h16
-rw-r--r--views/controls/button/button_dropdown.cc26
-rw-r--r--views/controls/combobox/native_combobox_views.cc23
-rw-r--r--views/controls/combobox/native_combobox_views.h5
-rw-r--r--views/controls/menu/menu_controller.cc30
-rw-r--r--views/controls/menu/menu_controller.h24
-rw-r--r--views/controls/menu/menu_controller_delegate.h41
-rw-r--r--views/controls/menu/menu_item_view.cc116
-rw-r--r--views/controls/menu/menu_item_view.h46
-rw-r--r--views/controls/menu/menu_model_adapter.cc6
-rw-r--r--views/controls/menu/menu_model_adapter.h4
-rw-r--r--views/controls/menu/menu_model_adapter_unittest.cc11
-rw-r--r--views/controls/menu/menu_runner.cc240
-rw-r--r--views/controls/menu/menu_runner.h78
-rw-r--r--views/controls/textfield/native_textfield_views.cc17
-rw-r--r--views/controls/textfield/native_textfield_views.h4
-rw-r--r--views/views.gyp1
56 files changed, 793 insertions, 567 deletions
diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc
index 1721a93..c25ccbd 100644
--- a/chrome/browser/chromeos/frame/browser_view.cc
+++ b/chrome/browser/chromeos/frame/browser_view.cc
@@ -41,6 +41,7 @@
#include "views/controls/button/image_button.h"
#include "views/controls/menu/menu_delegate.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/root_view.h"
#include "views/widget/widget.h"
#include "views/window/hit_test.h"
@@ -511,10 +512,11 @@ void BrowserView::ShowContextMenuForView(views::View* source,
if (hit_test == HTCAPTION || hit_test == HTNOWHERE) {
// rebuild menu so it reflects current application state
InitSystemMenu();
- system_menu_->RunMenuAt(source->GetWidget(), NULL,
- gfx::Rect(p, gfx::Size(0,0)),
- views::MenuItemView::TOPLEFT,
- true);
+ if (system_menu_runner_->RunMenuAt(source->GetWidget(), NULL,
+ gfx::Rect(p, gfx::Size(0,0)), views::MenuItemView::TOPLEFT,
+ views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
}
@@ -620,14 +622,16 @@ void BrowserView::GetAccessiblePanes(
// BrowserView private.
void BrowserView::InitSystemMenu() {
- system_menu_.reset(new views::MenuItemView(system_menu_delegate_.get()));
- system_menu_->AppendDelegateMenuItem(IDC_RESTORE_TAB);
-
- system_menu_->AppendMenuItemWithLabel(
+ views::MenuItemView* menu =
+ new views::MenuItemView(system_menu_delegate_.get());
+ // MenuRunner takes ownership of menu.
+ system_menu_runner_.reset(new views::MenuRunner(menu));
+ menu->AppendDelegateMenuItem(IDC_RESTORE_TAB);
+ menu->AppendMenuItemWithLabel(
IDC_NEW_TAB,
UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_TAB)));
- system_menu_->AppendSeparator();
- system_menu_->AppendMenuItemWithLabel(
+ menu->AppendSeparator();
+ menu->AppendMenuItemWithLabel(
IDC_TASK_MANAGER,
UTF16ToWide(l10n_util::GetStringUTF16(IDS_TASK_MANAGER)));
}
diff --git a/chrome/browser/chromeos/frame/browser_view.h b/chrome/browser/chromeos/frame/browser_view.h
index 0c24c6e..9d7ae95 100644
--- a/chrome/browser/chromeos/frame/browser_view.h
+++ b/chrome/browser/chromeos/frame/browser_view.h
@@ -28,6 +28,7 @@ class ImageButton;
class ImageView;
class MenuDelegate;
class MenuItemView;
+class MenuRunner;
} // namespace views
namespace chromeos {
@@ -129,7 +130,7 @@ class BrowserView : public ::BrowserView,
LayoutModeButton* layout_mode_button_;
// System menu.
- scoped_ptr<views::MenuItemView> system_menu_;
+ scoped_ptr<views::MenuRunner> system_menu_runner_;
scoped_ptr<views::MenuDelegate> system_menu_delegate_;
// Focused native widget before wrench menu shows up. We need this to properly
diff --git a/chrome/browser/chromeos/login/language_switch_menu.cc b/chrome/browser/chromeos/login/language_switch_menu.cc
index 50425be..c1c39d4 100644
--- a/chrome/browser/chromeos/login/language_switch_menu.cc
+++ b/chrome/browser/chromeos/login/language_switch_menu.cc
@@ -23,6 +23,7 @@
#include "ui/gfx/platform_font_gtk.h"
#include "views/controls/button/menu_button.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -38,7 +39,8 @@ const int kMoreLanguagesSubMenu = 200;
namespace chromeos {
LanguageSwitchMenu::LanguageSwitchMenu()
- : ALLOW_THIS_IN_INITIALIZER_LIST(menu_(new views::MenuItemView(this))) {
+ : ALLOW_THIS_IN_INITIALIZER_LIST(menu_(new views::MenuItemView(this))),
+ menu_runner_(new views::MenuRunner(menu_)) {
}
LanguageSwitchMenu::~LanguageSwitchMenu() {}
@@ -171,8 +173,10 @@ void LanguageSwitchMenu::RunMenu(views::View* source, const gfx::Point& pt) {
else
new_pt.set_x(pt.x() - reverse_offset);
- menu_->RunMenuAt(button->GetWidget(), button,
- gfx::Rect(new_pt, gfx::Size()), views::MenuItemView::TOPLEFT, true);
+ if (menu_runner_->RunMenuAt(button->GetWidget(), button,
+ gfx::Rect(new_pt, gfx::Size()), views::MenuItemView::TOPLEFT,
+ views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
+ return;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/login/language_switch_menu.h b/chrome/browser/chromeos/login/language_switch_menu.h
index 2b99a66..c1681f6 100644
--- a/chrome/browser/chromeos/login/language_switch_menu.h
+++ b/chrome/browser/chromeos/login/language_switch_menu.h
@@ -18,6 +18,7 @@ class WizardControllerTest_SwitchLanguage_Test;
namespace views {
class MenuItemView;
+class MenuRunner;
} // namespace views
namespace chromeos {
@@ -58,8 +59,11 @@ class LanguageSwitchMenu : public views::ViewMenuDelegate,
// views::MenuDelegate implementation.
virtual void ExecuteCommand(int command_id) OVERRIDE;
- // Dialog controls that we own ourselves.
- scoped_ptr<views::MenuItemView> menu_;
+ // The menu.
+ views::MenuItemView* menu_;
+
+ // Runs and owns |menu_|.
+ scoped_ptr<views::MenuRunner> menu_runner_;
// Language locale name storage.
scoped_ptr<LanguageList> language_list_;
diff --git a/chrome/browser/chromeos/notifications/balloon_view.cc b/chrome/browser/chromeos/notifications/balloon_view.cc
index 73a6906..b4fe5aa 100644
--- a/chrome/browser/chromeos/notifications/balloon_view.cc
+++ b/chrome/browser/chromeos/notifications/balloon_view.cc
@@ -34,6 +34,7 @@
#include "views/controls/label.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/view_menu_delegate.h"
#include "views/widget/widget.h"
@@ -120,17 +121,17 @@ class NotificationControlView : public views::View,
CreateOptionsMenu();
views::MenuModelAdapter menu_model_adapter(options_menu_contents_.get());
- views::MenuItemView menu(&menu_model_adapter);
- menu_model_adapter.BuildMenu(&menu);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
gfx::Point screen_location;
views::View::ConvertPointToScreen(options_menu_button_,
&screen_location);
- menu.RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
- options_menu_button_,
- gfx::Rect(screen_location, options_menu_button_->size()),
- views::MenuItemView::TOPRIGHT,
- true);
+ if (menu_runner.RunMenuAt(
+ source->GetWidget()->GetTopLevelWidget(), options_menu_button_,
+ gfx::Rect(screen_location, options_menu_button_->size()),
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
// views::ButtonListener implements.
diff --git a/chrome/browser/chromeos/status/clock_menu_button.cc b/chrome/browser/chromeos/status/clock_menu_button.cc
index 5e17066..3b0384c 100644
--- a/chrome/browser/chromeos/status/clock_menu_button.cc
+++ b/chrome/browser/chromeos/status/clock_menu_button.cc
@@ -20,6 +20,7 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
#include "unicode/datefmt.h"
@@ -155,12 +156,11 @@ void ClockMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- menu_->RunMenuAt(
- source->GetWidget()->GetTopLevelWidget(),
- this,
- bounds,
- views::MenuItemView::TOPRIGHT,
- true);
+ if (menu_runner_->RunMenuAt(
+ source->GetWidget()->GetTopLevelWidget(), this, bounds,
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
// ClockMenuButton, views::View implementation:
@@ -170,23 +170,26 @@ void ClockMenuButton::OnLocaleChanged() {
}
void ClockMenuButton::EnsureMenu() {
- if (!menu_.get()) {
- menu_.reset(new views::MenuItemView(this));
-
- // Text for this item will be set by GetLabel().
- menu_->AppendDelegateMenuItem(CLOCK_DISPLAY_ITEM);
-
- // If options dialog is unavailable, don't show a separator and configure
- // menu item.
- if (host_->ShouldOpenButtonOptions(this)) {
- menu_->AppendSeparator();
-
- const string16 clock_open_options_label =
- l10n_util::GetStringUTF16(IDS_STATUSBAR_CLOCK_OPEN_OPTIONS_DIALOG);
- menu_->AppendMenuItemWithLabel(
- CLOCK_OPEN_OPTIONS_ITEM,
- UTF16ToWide(clock_open_options_label));
- }
+ if (menu_runner_.get())
+ return;
+
+ views::MenuItemView* menu = new views::MenuItemView(this);
+ // menu_runner_ takes ownership of menu.
+ menu_runner_.reset(new views::MenuRunner(menu));
+
+ // Text for this item will be set by GetLabel().
+ menu->AppendDelegateMenuItem(CLOCK_DISPLAY_ITEM);
+
+ // If options dialog is unavailable, don't show a separator and configure
+ // menu item.
+ if (host_->ShouldOpenButtonOptions(this)) {
+ menu->AppendSeparator();
+
+ const string16 clock_open_options_label =
+ l10n_util::GetStringUTF16(IDS_STATUSBAR_CLOCK_OPEN_OPTIONS_DIALOG);
+ menu->AppendMenuItemWithLabel(
+ CLOCK_OPEN_OPTIONS_ITEM,
+ UTF16ToWide(clock_open_options_label));
}
}
diff --git a/chrome/browser/chromeos/status/clock_menu_button.h b/chrome/browser/chromeos/status/clock_menu_button.h
index 7864b97..ac712c7 100644
--- a/chrome/browser/chromeos/status/clock_menu_button.h
+++ b/chrome/browser/chromeos/status/clock_menu_button.h
@@ -21,7 +21,7 @@
#include "views/controls/menu/view_menu_delegate.h"
namespace views {
-class MenuItemView;
+class MenuRunner;
}
namespace chromeos {
@@ -80,9 +80,7 @@ class ClockMenuButton : public StatusAreaButton,
base::OneShotTimer<ClockMenuButton> timer_;
// The clock menu.
- // NOTE: we use a scoped_ptr here as menu calls into 'this' from the
- // constructor.
- scoped_ptr<views::MenuItemView> menu_;
+ scoped_ptr<views::MenuRunner> menu_runner_;
PrefChangeRegistrar registrar_;
diff --git a/chrome/browser/chromeos/status/input_method_menu.cc b/chrome/browser/chromeos/status/input_method_menu.cc
index 6c0fa3f..f73532a 100644
--- a/chrome/browser/chromeos/status/input_method_menu.cc
+++ b/chrome/browser/chromeos/status/input_method_menu.cc
@@ -25,6 +25,7 @@
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -139,6 +140,7 @@ InputMethodMenu::InputMethodMenu(PrefService* pref_service,
new views::MenuModelAdapter(this))),
input_method_menu_(
new views::MenuItemView(input_method_menu_delegate_.get())),
+ input_method_menu_runner_(new views::MenuRunner(input_method_menu_)),
minimum_input_method_menu_width_(0),
menu_alignment_(views::MenuItemView::TOPRIGHT),
pref_service_(pref_service),
@@ -388,8 +390,11 @@ void InputMethodMenu::RunMenu(views::View* source, const gfx::Point& pt) {
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- input_method_menu_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
- NULL, bounds, menu_alignment_, true);
+ if (input_method_menu_runner_->RunMenuAt(
+ source->GetWidget()->GetTopLevelWidget(), NULL, bounds,
+ menu_alignment_, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
////////////////////////////////////////////////////////////////////////////////
@@ -536,7 +541,7 @@ void InputMethodMenu::RebuildModel() {
}
// Rebuild the menu from the model.
- input_method_menu_delegate_->BuildMenu(input_method_menu_.get());
+ input_method_menu_delegate_->BuildMenu(input_method_menu_);
}
bool InputMethodMenu::IndexIsInInputMethodList(int index) const {
diff --git a/chrome/browser/chromeos/status/input_method_menu.h b/chrome/browser/chromeos/status/input_method_menu.h
index be736d9..0382380 100644
--- a/chrome/browser/chromeos/status/input_method_menu.h
+++ b/chrome/browser/chromeos/status/input_method_menu.h
@@ -28,6 +28,7 @@ class SimpleMenuModel;
namespace views {
class MenuItemView;
class MenuModelAdapter;
+class MenuRunner;
} // namespace views
namespace chromeos {
@@ -173,7 +174,8 @@ class InputMethodMenu : public views::ViewMenuDelegate,
// views::MenuDelegate interface required for MenuItemView.
scoped_ptr<ui::SimpleMenuModel> model_;
scoped_ptr<views::MenuModelAdapter> input_method_menu_delegate_;
- scoped_ptr<views::MenuItemView> input_method_menu_;
+ views::MenuItemView* input_method_menu_;
+ scoped_ptr<views::MenuRunner> input_method_menu_runner_;
int minimum_input_method_menu_width_;
diff --git a/chrome/browser/chromeos/status/memory_menu_button.cc b/chrome/browser/chromeos/status/memory_menu_button.cc
index 02f3495..291fa67 100644
--- a/chrome/browser/chromeos/status/memory_menu_button.cc
+++ b/chrome/browser/chromeos/status/memory_menu_button.cc
@@ -15,6 +15,7 @@
#include "content/common/notification_service.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
#if defined(USE_TCMALLOC)
@@ -211,38 +212,35 @@ void MemoryMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
// View passed in must be a views::MenuButton, i.e. the MemoryMenuButton.
DCHECK_EQ(source, this);
- EnsureMenu();
-
+ views::MenuRunner menu_runner(CreateMenu());
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- menu_->RunMenuAt(
- source->GetWidget()->GetTopLevelWidget(),
- this,
- bounds,
- views::MenuItemView::TOPRIGHT,
- true);
+ if (menu_runner.RunMenuAt(
+ source->GetWidget()->GetTopLevelWidget(), this, bounds,
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
-// MemoryMenuButton, views::View implementation:
-
-void MemoryMenuButton::EnsureMenu() {
+views::MenuItemView* MemoryMenuButton::CreateMenu() {
// Just rebuild the menu each time to ensure the labels are up-to-date.
- menu_.reset(new views::MenuItemView(this));
+ views::MenuItemView* menu = new views::MenuItemView(this);
// Text for these items will be set by GetLabel().
- menu_->AppendDelegateMenuItem(MEM_TOTAL_ITEM);
- menu_->AppendDelegateMenuItem(MEM_FREE_ITEM);
- menu_->AppendDelegateMenuItem(MEM_BUFFERS_ITEM);
- menu_->AppendDelegateMenuItem(MEM_CACHE_ITEM);
- menu_->AppendDelegateMenuItem(SHMEM_ITEM);
- menu_->AppendSeparator();
- menu_->AppendDelegateMenuItem(PURGE_MEMORY_ITEM);
+ menu->AppendDelegateMenuItem(MEM_TOTAL_ITEM);
+ menu->AppendDelegateMenuItem(MEM_FREE_ITEM);
+ menu->AppendDelegateMenuItem(MEM_BUFFERS_ITEM);
+ menu->AppendDelegateMenuItem(MEM_CACHE_ITEM);
+ menu->AppendDelegateMenuItem(SHMEM_ITEM);
+ menu->AppendSeparator();
+ menu->AppendDelegateMenuItem(PURGE_MEMORY_ITEM);
#if defined(USE_TCMALLOC)
- menu_->AppendSeparator();
- menu_->AppendDelegateMenuItem(TOGGLE_PROFILING_ITEM);
+ menu->AppendSeparator();
+ menu->AppendDelegateMenuItem(TOGGLE_PROFILING_ITEM);
if (IsHeapProfilerRunning())
- menu_->AppendDelegateMenuItem(DUMP_PROFILING_ITEM);
+ menu->AppendDelegateMenuItem(DUMP_PROFILING_ITEM);
#endif
+ return menu;
}
/////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/status/memory_menu_button.h b/chrome/browser/chromeos/status/memory_menu_button.h
index 7e2c83a..2802c26 100644
--- a/chrome/browser/chromeos/status/memory_menu_button.h
+++ b/chrome/browser/chromeos/status/memory_menu_button.h
@@ -20,6 +20,7 @@ struct SystemMemoryInfoKB;
namespace views {
class MenuItemView;
+class MenuRunner;
}
namespace chromeos {
@@ -58,18 +59,14 @@ class MemoryMenuButton : public StatusAreaButton,
// Execute command id for each renderer. Used for heap profiling.
void SendCommandToRenderers(int id);
- // Create and initialize menu if not already present.
- void EnsureMenu();
+ // Creates and returns the menu. The caller owns the returned value.
+ views::MenuItemView* CreateMenu();
// Updates text and schedules the timer to fire at the next minute interval.
void UpdateTextAndSetNextTimer();
base::OneShotTimer<MemoryMenuButton> timer_;
- // NOTE: we use a scoped_ptr here as menu calls into 'this' from the
- // constructor.
- scoped_ptr<views::MenuItemView> menu_;
-
// Raw data from /proc/meminfo
scoped_ptr<base::SystemMemoryInfoKB> meminfo_;
diff --git a/chrome/browser/chromeos/status/network_menu.cc b/chrome/browser/chromeos/status/network_menu.cc
index 7aeb2db..61901df3 100644
--- a/chrome/browser/chromeos/status/network_menu.cc
+++ b/chrome/browser/chromeos/status/network_menu.cc
@@ -37,6 +37,7 @@
#include "ui/gfx/skbitmap_operations.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -1029,11 +1030,12 @@ NetworkMenu::NetworkMenu(Delegate* delegate, bool is_browser_mode)
: delegate_(delegate),
is_browser_mode_(is_browser_mode),
refreshing_menu_(false),
+ menu_item_view_(NULL),
min_width_(kDefaultMinimumWidth) {
main_menu_model_.reset(new MainMenuModel(this));
menu_model_adapter_.reset(
new views::MenuModelAdapter(main_menu_model_.get()));
- menu_item_view_.reset(new views::MenuItemView(menu_model_adapter_.get()));
+ menu_item_view_ = new views::MenuItemView(menu_model_adapter_.get());
menu_item_view_->set_has_icons(true);
menu_item_view_->set_menu_position(
views::MenuItemView::POSITION_BELOW_BOUNDS);
@@ -1047,7 +1049,7 @@ ui::MenuModel* NetworkMenu::GetMenuModel() {
}
void NetworkMenu::CancelMenu() {
- menu_item_view_->Cancel();
+ menu_runner_->Cancel();
}
void NetworkMenu::UpdateMenu() {
@@ -1057,8 +1059,8 @@ void NetworkMenu::UpdateMenu() {
main_menu_model_->InitMenuItems(
is_browser_mode(), delegate_->ShouldOpenButtonOptions());
- menu_model_adapter_->BuildMenu(menu_item_view_.get());
- SetMenuMargins(menu_item_view_.get(), kTopMargin, kBottomMargin);
+ menu_model_adapter_->BuildMenu(menu_item_view_);
+ SetMenuMargins(menu_item_view_, kTopMargin, kBottomMargin);
menu_item_view_->ChildrenChanged();
refreshing_menu_ = false;
@@ -1074,9 +1076,10 @@ void NetworkMenu::RunMenu(views::View* source) {
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
menu_item_view_->GetSubmenu()->set_minimum_preferred_width(min_width_);
- menu_item_view_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
- delegate_->GetMenuButton(), bounds,
- views::MenuItemView::TOPRIGHT, true);
+ if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
+ delegate_->GetMenuButton(), bounds, views::MenuItemView::TOPRIGHT,
+ views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
+ return;
}
void NetworkMenu::ShowTabbedNetworkSettings(const Network* network) const {
diff --git a/chrome/browser/chromeos/status/network_menu.h b/chrome/browser/chromeos/status/network_menu.h
index 75e932a..980e25c 100644
--- a/chrome/browser/chromeos/status/network_menu.h
+++ b/chrome/browser/chromeos/status/network_menu.h
@@ -36,6 +36,7 @@ namespace views {
class MenuItemView;
class MenuButton;
class MenuModelAdapter;
+class MenuRunner;
}
namespace chromeos {
@@ -123,7 +124,8 @@ class NetworkMenu {
// The network menu.
scoped_ptr<NetworkMenuModel> main_menu_model_;
scoped_ptr<views::MenuModelAdapter> menu_model_adapter_;
- scoped_ptr<views::MenuItemView> menu_item_view_;
+ views::MenuItemView* menu_item_view_;
+ scoped_ptr<views::MenuRunner> menu_runner_;
// Holds minimum width of the menu.
int min_width_;
diff --git a/chrome/browser/chromeos/status/power_menu_button.cc b/chrome/browser/chromeos/status/power_menu_button.cc
index 22f971e..05578636 100644
--- a/chrome/browser/chromeos/status/power_menu_button.cc
+++ b/chrome/browser/chromeos/status/power_menu_button.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/auto_reset.h"
#include "base/string_number_conversions.h"
#include "base/stringprintf.h"
#include "base/time.h"
@@ -19,6 +20,7 @@
#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/font.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -220,8 +222,8 @@ class PowerMenuButton::StatusView : public View {
void OnMouseReleased(const views::MouseEvent& event) {
if (event.IsLeftMouseButton()) {
- DCHECK(menu_button_->menu_);
- menu_button_->menu_->Cancel();
+ DCHECK(menu_button_->menu_runner_);
+ menu_button_->menu_runner_->Cancel();
}
}
@@ -243,7 +245,7 @@ PowerMenuButton::PowerMenuButton(StatusAreaHost* host)
battery_time_to_full_(TimeDelta::FromMicroseconds(kInitialMS)),
battery_time_to_empty_(TimeDelta::FromMicroseconds(kInitialMS)),
status_(NULL),
- menu_(NULL) {
+ menu_runner_(NULL) {
UpdateIconAndLabelInfo();
CrosLibrary::Get()->GetPowerLibrary()->AddObserver(this);
}
@@ -315,31 +317,31 @@ void PowerMenuButton::OnLocaleChanged() {
// PowerMenuButton, views::ViewMenuDelegate implementation:
void PowerMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
- menu_ = new views::MenuItemView(this);
- views::MenuItemView* submenu =
- menu_->AppendMenuItem(
+ views::MenuItemView* menu = new views::MenuItemView(this);
+ // MenuRunner takes ownership of |menu|.
+ views::MenuRunner menu_runner(menu);
+ views::MenuItemView* submenu = menu->AppendMenuItem(
POWER_BATTERY_PERCENTAGE_ITEM,
std::wstring(),
views::MenuItemView::NORMAL);
status_ = new StatusView(this);
submenu->AddChildView(status_);
- menu_->CreateSubmenu()->set_resize_open_menu(true);
- menu_->SetMargins(0, 0);
+ menu->CreateSubmenu()->set_resize_open_menu(true);
+ menu->SetMargins(0, 0);
submenu->SetMargins(0, 0);
- menu_->ChildrenChanged();
+ menu->ChildrenChanged();
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- menu_->RunMenuAt(
- source->GetWidget()->GetTopLevelWidget(),
- this,
- bounds,
- views::MenuItemView::TOPRIGHT,
- true);
- delete menu_;
+ AutoReset<views::MenuRunner*> menu_runner_reseter(&menu_runner_,
+ &menu_runner);
+ if (menu_runner.RunMenuAt(
+ source->GetWidget()->GetTopLevelWidget(), this, bounds,
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
status_ = NULL;
- menu_ = NULL;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chromeos/status/power_menu_button.h b/chrome/browser/chromeos/status/power_menu_button.h
index f27f07e..aef5aec7 100644
--- a/chrome/browser/chromeos/status/power_menu_button.h
+++ b/chrome/browser/chromeos/status/power_menu_button.h
@@ -15,6 +15,10 @@ namespace base {
class TimeDelta;
}
+namespace views {
+class MenuRunner;
+}
+
class SkBitmap;
namespace chromeos {
@@ -75,8 +79,8 @@ class PowerMenuButton : public StatusAreaButton,
// The currently showing status view. NULL if menu is not being displayed.
StatusView* status_;
- // The currently showing menu. NULL if menu is not being displayed.
- views::MenuItemView* menu_;
+ // If non-null the menu is showing.
+ views::MenuRunner* menu_runner_;
DISALLOW_COPY_AND_ASSIGN(PowerMenuButton);
};
diff --git a/chrome/browser/ui/panels/panel_browser_frame_view.cc b/chrome/browser/ui/panels/panel_browser_frame_view.cc
index 88f1705..ef907fd 100644
--- a/chrome/browser/ui/panels/panel_browser_frame_view.cc
+++ b/chrome/browser/ui/panels/panel_browser_frame_view.cc
@@ -214,7 +214,8 @@ PanelBrowserFrameView::PanelBrowserFrameView(BrowserFrame* frame,
title_label_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(settings_menu_contents_(this)),
settings_menu_adapter_(&settings_menu_contents_),
- settings_menu_(&settings_menu_adapter_) {
+ settings_menu_(new views::MenuItemView(&settings_menu_adapter_)),
+ settings_menu_runner_(settings_menu_) {
EnsureResourcesInitialized();
frame_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
@@ -442,9 +443,11 @@ void PanelBrowserFrameView::RunMenu(View* source, const gfx::Point& pt) {
DCHECK_EQ(settings_button_, source);
gfx::Point screen_point;
views::View::ConvertPointToScreen(source, &screen_point);
- settings_menu_.RunMenuAt(source->GetWidget(),
- settings_button_, gfx::Rect(screen_point, source->size()),
- views::MenuItemView::TOPRIGHT, true);
+ if (settings_menu_runner_.RunMenuAt(source->GetWidget(),
+ settings_button_, gfx::Rect(screen_point, source->size()),
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
bool PanelBrowserFrameView::IsCommandIdChecked(int command_id) const {
@@ -750,5 +753,5 @@ void PanelBrowserFrameView::EnsureSettingsMenuCreated() {
settings_menu_contents_.AddItem(
COMMAND_MANAGE, l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS));
- settings_menu_adapter_.BuildMenu(&settings_menu_);
+ settings_menu_adapter_.BuildMenu(settings_menu_);
}
diff --git a/chrome/browser/ui/panels/panel_browser_frame_view.h b/chrome/browser/ui/panels/panel_browser_frame_view.h
index 16b7efa..fa98d54 100644
--- a/chrome/browser/ui/panels/panel_browser_frame_view.h
+++ b/chrome/browser/ui/panels/panel_browser_frame_view.h
@@ -16,6 +16,7 @@
#include "views/controls/button/button.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/view_menu_delegate.h"
class Extension;
@@ -193,7 +194,9 @@ class PanelBrowserFrameView : public BrowserNonClientFrameView,
scoped_ptr<MouseWatcher> mouse_watcher_;
ui::SimpleMenuModel settings_menu_contents_;
views::MenuModelAdapter settings_menu_adapter_;
- views::MenuItemView settings_menu_;
+ // Owned by |settings_menu_runner_|.
+ views::MenuItemView* settings_menu_;
+ views::MenuRunner settings_menu_runner_;
scoped_ptr<ExtensionUninstallDialog> extension_uninstall_dialog_;
DISALLOW_COPY_AND_ASSIGN(PanelBrowserFrameView);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_context_menu.cc b/chrome/browser/ui/views/bookmarks/bookmark_context_menu.cc
index 0a4633f..e908105 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_context_menu.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_context_menu.cc
@@ -14,6 +14,7 @@
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
////////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,7 @@ BookmarkContextMenu::BookmarkContextMenu(
this, profile, page_navigator, parent, selection))),
parent_widget_(parent_widget),
ALLOW_THIS_IN_INITIALIZER_LIST(menu_(new views::MenuItemView(this))),
+ menu_runner_(new views::MenuRunner(menu_)),
parent_node_(parent),
observer_(NULL),
close_on_remove_(close_on_remove) {
@@ -46,10 +48,12 @@ void BookmarkContextMenu::RunMenuAt(const gfx::Point& point) {
Source<BookmarkContextMenu>(this),
NotificationService::NoDetails());
// width/height don't matter here.
- views::MenuItemView::AnchorPosition anchor = base::i18n::IsRTL() ?
- views::MenuItemView::TOPRIGHT : views::MenuItemView::TOPLEFT;
- menu_->RunMenuAt(parent_widget_, NULL, gfx::Rect(point.x(), point.y(), 0, 0),
- anchor, true);
+ if (menu_runner_->RunMenuAt(
+ parent_widget_, NULL, gfx::Rect(point.x(), point.y(), 0, 0),
+ views::MenuItemView::TOPLEFT,
+ (views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::IS_NESTED)) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_context_menu.h b/chrome/browser/ui/views/bookmarks/bookmark_context_menu.h
index de732d1..064eb0d 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_context_menu.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_context_menu.h
@@ -10,6 +10,7 @@
#include "views/controls/menu/menu_delegate.h"
namespace views {
+class MenuRunner;
class Widget;
}
@@ -42,7 +43,7 @@ class BookmarkContextMenu : public BookmarkContextMenuControllerViewsDelegate,
// Shows the context menu at the specified point.
void RunMenuAt(const gfx::Point& point);
- views::MenuItemView* menu() const { return menu_.get(); }
+ views::MenuItemView* menu() const { return menu_; }
void set_observer(BookmarkContextMenuObserver* observer) {
observer_ = observer;
@@ -69,8 +70,11 @@ class BookmarkContextMenu : public BookmarkContextMenuControllerViewsDelegate,
// The parent of dialog boxes opened from the context menu.
views::Widget* parent_widget_;
- // The menu itself.
- scoped_ptr<views::MenuItemView> menu_;
+ // The menu itself. This is owned by |menu_runner_|.
+ views::MenuItemView* menu_;
+
+ // Responsible for running the menu.
+ scoped_ptr<views::MenuRunner> menu_runner_;
// The node we're showing the menu for.
const BookmarkNode* parent_node_;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
index d6f5e81..964bc6f 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
@@ -24,6 +24,7 @@
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/controls/button/menu_button.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
using views::MenuItemView;
@@ -40,6 +41,7 @@ BookmarkMenuController::BookmarkMenuController(Profile* profile,
bookmark_bar_(NULL) {
menu_delegate_->Init(this, NULL, node, start_child_index,
BookmarkMenuDelegate::HIDE_OTHER_FOLDER);
+ menu_runner_.reset(new views::MenuRunner(menu_delegate_->menu()));
}
void BookmarkMenuController::RunMenuAt(BookmarkBarView* bookmark_bar,
@@ -49,27 +51,19 @@ void BookmarkMenuController::RunMenuAt(BookmarkBarView* bookmark_bar,
DCHECK(menu_button);
MenuItemView::AnchorPosition anchor;
bookmark_bar_->GetAnchorPositionForButton(menu_button, &anchor);
- RunMenuAt(menu_button, anchor, for_drop);
-}
-
-void BookmarkMenuController::RunMenuAt(
- views::MenuButton* button,
- MenuItemView::AnchorPosition position,
- bool for_drop) {
gfx::Point screen_loc;
- views::View::ConvertPointToScreen(button, &screen_loc);
+ views::View::ConvertPointToScreen(menu_button, &screen_loc);
// Subtract 1 from the height to make the popup flush with the button border.
- gfx::Rect bounds(screen_loc.x(), screen_loc.y(), button->width(),
- button->height() - 1);
+ gfx::Rect bounds(screen_loc.x(), screen_loc.y(), menu_button->width(),
+ menu_button->height() - 1);
for_drop_ = for_drop;
menu_delegate_->profile()->GetBookmarkModel()->AddObserver(this);
- if (for_drop) {
- menu()->RunMenuForDropAt(menu_delegate_->parent(), bounds, position);
- } else {
- menu()->RunMenuAt(menu_delegate_->parent(), button, bounds, position,
- false);
+ // We only delete ourself after the menu completes, so we can safely ignore
+ // the return value.
+ ignore_result(menu_runner_->RunMenuAt(menu_delegate_->parent(), menu_button,
+ bounds, anchor, for_drop ? views::MenuRunner::FOR_DROP : 0));
+ if (!for_drop)
delete this;
- }
}
void BookmarkMenuController::Cancel() {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
index 770d2b8..f8b8227 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
@@ -29,6 +29,7 @@ class OSExchangeData;
namespace views {
class MenuButton;
+class MenuRunner;
class Widget;
} // namespace views
@@ -58,11 +59,6 @@ class BookmarkMenuController : public BaseBookmarkModelObserver,
void RunMenuAt(BookmarkBarView* bookmark_bar, bool for_drop);
- // Shows the menu.
- void RunMenuAt(views::MenuButton* button,
- views::MenuItemView::AnchorPosition position,
- bool for_drop);
-
// Hides the menu.
void Cancel();
@@ -119,6 +115,8 @@ class BookmarkMenuController : public BaseBookmarkModelObserver,
// BookmarkMenuController deletes itself as necessary.
virtual ~BookmarkMenuController();
+ scoped_ptr<views::MenuRunner> menu_runner_;
+
scoped_ptr<BookmarkMenuDelegate> menu_delegate_;
// The node we're showing the contents of.
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
index 7e1731b..fe3e988 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
-#include "base/stl_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_node_data.h"
@@ -51,7 +50,6 @@ BookmarkMenuDelegate::BookmarkMenuDelegate(Profile* profile,
BookmarkMenuDelegate::~BookmarkMenuDelegate() {
profile_->GetBookmarkModel()->RemoveObserver(this);
- STLDeleteValues(&node_to_menu_map_);
}
void BookmarkMenuDelegate::Init(views::MenuDelegate* real_delegate,
@@ -313,9 +311,56 @@ void BookmarkMenuDelegate::WillRemoveBookmarks(
const std::vector<const BookmarkNode*>& bookmarks) {
DCHECK(!is_mutating_model_);
is_mutating_model_ = true;
- std::set<MenuItemView*> removed_menus;
- WillRemoveBookmarksImpl(bookmarks, &removed_menus);
- STLDeleteElements(&removed_menus);
+
+ // Remove the observer so that when the remove happens we don't prematurely
+ // cancel the menu. The observer ias added back in DidRemoveBookmarks.
+ profile_->GetBookmarkModel()->RemoveObserver(this);
+
+ // Remove the menu items.
+ std::set<MenuItemView*> changed_parent_menus;
+ for (std::vector<const BookmarkNode*>::const_iterator i = bookmarks.begin();
+ i != bookmarks.end(); ++i) {
+ NodeToMenuIDMap::iterator node_to_menu = node_to_menu_id_map_.find(*i);
+ if (node_to_menu != node_to_menu_id_map_.end()) {
+ MenuItemView* menu = GetMenuByID(node_to_menu->second);
+ DCHECK(menu); // If there an entry in node_to_menu_id_map_, there should
+ // be a menu.
+ DCHECK(menu->GetParentMenuItem());
+ changed_parent_menus.insert(menu->GetParentMenuItem());
+ menu->GetParentMenuItem()->RemoveMenuItemAt(
+ menu->parent()->GetIndexOf(menu));
+ node_to_menu_id_map_.erase(node_to_menu);
+ }
+ }
+
+ // All the bookmarks in |bookmarks| should have the same parent. It's possible
+ // to support different parents, but this would need to prune any nodes whose
+ // parent has been removed. As all nodes currently have the same parent, there
+ // is the DCHECK.
+ DCHECK(changed_parent_menus.size() <= 1);
+
+ for (std::set<MenuItemView*>::const_iterator i = changed_parent_menus.begin();
+ i != changed_parent_menus.end(); ++i) {
+ (*i)->ChildrenChanged();
+ }
+
+ // Remove any descendants of the removed nodes in node_to_menu_id_map_.
+ for (NodeToMenuIDMap::iterator i = node_to_menu_id_map_.begin();
+ i != node_to_menu_id_map_.end(); ) {
+ bool ancestor_removed = false;
+ for (std::vector<const BookmarkNode*>::const_iterator j = bookmarks.begin();
+ j != bookmarks.end(); ++j) {
+ if (i->first->HasAncestor(*j)) {
+ ancestor_removed = true;
+ break;
+ }
+ }
+ if (ancestor_removed) {
+ node_to_menu_id_map_.erase(i++);
+ } else {
+ ++i;
+ }
+ }
}
void BookmarkMenuDelegate::DidRemoveBookmarks() {
@@ -396,56 +441,3 @@ MenuItemView* BookmarkMenuDelegate::GetMenuByID(int id) {
return parent_menu_item_ ? parent_menu_item_->GetMenuItemByID(id) : NULL;
}
-
-void BookmarkMenuDelegate::WillRemoveBookmarksImpl(
- const std::vector<const BookmarkNode*>& bookmarks,
- std::set<views::MenuItemView*>* removed_menus) {
- // Remove the observer so that when the remove happens we don't prematurely
- // cancel the menu. The observer ias added back in DidRemoveBookmarks.
- profile_->GetBookmarkModel()->RemoveObserver(this);
-
- // Remove the menu items.
- std::set<MenuItemView*> changed_parent_menus;
- for (std::vector<const BookmarkNode*>::const_iterator i = bookmarks.begin();
- i != bookmarks.end(); ++i) {
- NodeToMenuIDMap::iterator node_to_menu = node_to_menu_id_map_.find(*i);
- if (node_to_menu != node_to_menu_id_map_.end()) {
- MenuItemView* menu = GetMenuByID(node_to_menu->second);
- DCHECK(menu); // If there an entry in node_to_menu_id_map_, there should
- // be a menu.
- removed_menus->insert(menu);
- changed_parent_menus.insert(menu->GetParentMenuItem());
- menu->parent()->RemoveChildView(menu);
- node_to_menu_id_map_.erase(node_to_menu);
- }
- }
-
- // All the bookmarks in |bookmarks| should have the same parent. It's possible
- // to support different parents, but this would need to prune any nodes whose
- // parent has been removed. As all nodes currently have the same parent, there
- // is the DCHECK.
- DCHECK(changed_parent_menus.size() <= 1);
-
- for (std::set<MenuItemView*>::const_iterator i = changed_parent_menus.begin();
- i != changed_parent_menus.end(); ++i) {
- (*i)->ChildrenChanged();
- }
-
- // Remove any descendants of the removed nodes in node_to_menu_id_map_.
- for (NodeToMenuIDMap::iterator i = node_to_menu_id_map_.begin();
- i != node_to_menu_id_map_.end(); ) {
- bool ancestor_removed = false;
- for (std::vector<const BookmarkNode*>::const_iterator j = bookmarks.begin();
- j != bookmarks.end(); ++j) {
- if (i->first->HasAncestor(*j)) {
- ancestor_removed = true;
- break;
- }
- }
- if (ancestor_removed) {
- node_to_menu_id_map_.erase(i++);
- } else {
- ++i;
- }
- }
-}
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
index 9ad6ca6..35ec1ec 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
@@ -141,13 +141,6 @@ class BookmarkMenuDelegate : public BaseBookmarkModelObserver,
// Returns the menu whose id is |id|.
views::MenuItemView* GetMenuByID(int id);
- // Does the work of processing WillRemoveBookmarks. On exit the set of removed
- // menus is added to |removed_menus|. It's up to the caller to delete the
- // the menus added to |removed_menus|.
- void WillRemoveBookmarksImpl(
- const std::vector<const BookmarkNode*>& bookmarks,
- std::set<views::MenuItemView*>* removed_menus);
-
Profile* profile_;
PageNavigator* page_navigator_;
diff --git a/chrome/browser/ui/views/browser_actions_container.cc b/chrome/browser/ui/views/browser_actions_container.cc
index 1980660..f49cb4c 100644
--- a/chrome/browser/ui/views/browser_actions_container.cc
+++ b/chrome/browser/ui/views/browser_actions_container.cc
@@ -46,6 +46,7 @@
#include "views/controls/button/text_button.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/drag_utils.h"
#include "views/metrics.h"
@@ -255,14 +256,15 @@ void BrowserActionButton::ShowContextMenu(const gfx::Point& p,
scoped_refptr<ExtensionContextMenuModel> context_menu_contents_(
new ExtensionContextMenuModel(extension(), panel_->browser(), panel_));
views::MenuModelAdapter menu_model_adapter(context_menu_contents_.get());
- views::MenuItemView menu(&menu_model_adapter);
- menu_model_adapter.BuildMenu(&menu);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
- context_menu_ = &menu;
+ context_menu_ = menu_runner.GetMenu();
gfx::Point screen_loc;
views::View::ConvertPointToScreen(this, &screen_loc);
- context_menu_->RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()),
- views::MenuItemView::TOPLEFT, true);
+ if (menu_runner.RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()),
+ views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
SetButtonNotPushed();
context_menu_ = NULL;
diff --git a/chrome/browser/ui/views/compact_nav/compact_options_bar.cc b/chrome/browser/ui/views/compact_nav/compact_options_bar.cc
index bcf5b14..5ba008f 100644
--- a/chrome/browser/ui/views/compact_nav/compact_options_bar.cc
+++ b/chrome/browser/ui/views/compact_nav/compact_options_bar.cc
@@ -94,7 +94,7 @@ void CompactOptionsBar::RunMenu(views::View* source,
const gfx::Point& /* pt */) {
DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
- wrench_menu_ = new WrenchMenu(browser_view_->browser());
+ wrench_menu_.reset(new WrenchMenu(browser_view_->browser()));
wrench_menu_->Init(wrench_menu_model_.get());
for (size_t i = 0; i < menu_listeners_.size(); ++i)
diff --git a/chrome/browser/ui/views/compact_nav/compact_options_bar.h b/chrome/browser/ui/views/compact_nav/compact_options_bar.h
index 5cff968..13e1518 100644
--- a/chrome/browser/ui/views/compact_nav/compact_options_bar.h
+++ b/chrome/browser/ui/views/compact_nav/compact_options_bar.h
@@ -70,7 +70,7 @@ class CompactOptionsBar : public views::View,
// Button and models for the wrench/app menu.
views::MenuButton* app_menu_;
scoped_ptr<ui::SimpleMenuModel> wrench_menu_model_;
- scoped_refptr<WrenchMenu> wrench_menu_;
+ scoped_ptr<WrenchMenu> wrench_menu_;
std::vector<views::MenuListener*> menu_listeners_;
// TODO(stevet): Add the BrowserActionsContainer.
diff --git a/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.cc b/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.cc
index 621886d..b1472cf 100644
--- a/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.cc
+++ b/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.cc
@@ -15,6 +15,7 @@
#include "ui/gfx/canvas_skia.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -26,10 +27,12 @@ BrowserActionOverflowMenuController::BrowserActionOverflowMenuController(
: owner_(owner),
observer_(NULL),
menu_button_(menu_button),
+ menu_(NULL),
views_(&views),
start_index_(start_index),
for_drop_(false) {
- menu_.reset(new views::MenuItemView(this));
+ menu_ = new views::MenuItemView(this);
+ menu_runner_.reset(new views::MenuRunner(menu_));
menu_->set_has_icons(true);
size_t command_id = 1; // Menu id 0 is reserved, start with 1.
@@ -67,10 +70,10 @@ bool BrowserActionOverflowMenuController::RunMenu(views::Widget* window,
bounds.set_y(screen_loc.y());
views::MenuItemView::AnchorPosition anchor = views::MenuItemView::TOPRIGHT;
- if (for_drop) {
- menu_->RunMenuForDropAt(window, bounds, anchor);
- } else {
- menu_->RunMenuAt(window, menu_button_, bounds, anchor, false);
+ // As we maintain our own lifetime we can safely ignore the result.
+ ignore_result(menu_runner_->RunMenuAt(window, menu_button_, bounds, anchor,
+ for_drop_ ? views::MenuRunner::FOR_DROP : 0));
+ if (!for_drop_) {
// Give the context menu (if any) a chance to execute the user-selected
// command.
MessageLoop::current()->DeleteSoon(FROM_HERE, this);
@@ -102,12 +105,14 @@ bool BrowserActionOverflowMenuController::ShowContextMenu(
new ExtensionContextMenuModel(extension, owner_->browser(), owner_);
views::MenuModelAdapter context_menu_model_adapter(
context_menu_contents.get());
- views::MenuItemView context_menu(&context_menu_model_adapter);
- context_menu_model_adapter.BuildMenu(&context_menu);
+ views::MenuRunner context_menu_runner(
+ context_menu_model_adapter.CreateMenu());
+ // We can ignore the result as we delete ourself.
// This blocks until the user choses something or dismisses the menu.
- context_menu.RunMenuAt(menu_button_->GetWidget(),
- NULL, gfx::Rect(p, gfx::Size()), views::MenuItemView::TOPLEFT, true);
+ ignore_result(context_menu_runner.RunMenuAt(menu_button_->GetWidget(),
+ NULL, gfx::Rect(p, gfx::Size()), views::MenuItemView::TOPLEFT,
+ views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::IS_NESTED));
// The user is done with the context menu, so we can close the underlying
// menu.
diff --git a/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h b/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h
index d9c4e75..9a83432 100644
--- a/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h
+++ b/chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h
@@ -17,6 +17,7 @@ class BrowserActionsContainer;
class BrowserActionView;
namespace views {
+class MenuRunner;
class Widget;
}
@@ -90,8 +91,11 @@ class BrowserActionOverflowMenuController : public views::MenuDelegate {
// A pointer to the overflow menu button that we are showing the menu for.
views::MenuButton* menu_button_;
- // The overflow menu for the menu button.
- scoped_ptr<views::MenuItemView> menu_;
+ // The overflow menu for the menu button. Owned by |menu_runner_|.
+ views::MenuItemView* menu_;
+
+ // Resposible for running the menu.
+ scoped_ptr<views::MenuRunner> menu_runner_;
// The views vector of all the browser actions the container knows about. We
// won't show all items, just the one starting at |start_index| and above.
diff --git a/chrome/browser/ui/views/infobars/after_translate_infobar.cc b/chrome/browser/ui/views/infobars/after_translate_infobar.cc
index 3ce1c3f..6100958 100644
--- a/chrome/browser/ui/views/infobars/after_translate_infobar.cc
+++ b/chrome/browser/ui/views/infobars/after_translate_infobar.cc
@@ -12,6 +12,7 @@
#include "views/controls/label.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
AfterTranslateInfoBar::AfterTranslateInfoBar(
@@ -176,8 +177,10 @@ void AfterTranslateInfoBar::RunMenu(View* source, const gfx::Point& pt) {
}
views::MenuModelAdapter menu_model_adapter(menu_model);
- views::MenuItemView menu(&menu_model_adapter);
- menu_model_adapter.BuildMenu(&menu);
- menu.RunMenuAt(source->GetWidget(), NULL, gfx::Rect(pt, gfx::Size()),
- views::MenuItemView::TOPRIGHT, true);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
+ if (menu_runner.RunMenuAt(
+ source->GetWidget(), NULL, gfx::Rect(pt, gfx::Size()),
+ views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
diff --git a/chrome/browser/ui/views/infobars/before_translate_infobar.cc b/chrome/browser/ui/views/infobars/before_translate_infobar.cc
index 9a0f66e..61a6b12 100644
--- a/chrome/browser/ui/views/infobars/before_translate_infobar.cc
+++ b/chrome/browser/ui/views/infobars/before_translate_infobar.cc
@@ -12,6 +12,7 @@
#include "views/controls/label.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
BeforeTranslateInfoBar::BeforeTranslateInfoBar(
@@ -197,8 +198,9 @@ void BeforeTranslateInfoBar::RunMenu(View* source, const gfx::Point& pt) {
}
views::MenuModelAdapter menu_model_adapter(menu_model);
- views::MenuItemView menu(&menu_model_adapter);
- menu_model_adapter.BuildMenu(&menu);
- menu.RunMenuAt(source->GetWidget(), NULL, gfx::Rect(pt, gfx::Size()),
- views::MenuItemView::TOPRIGHT, true);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
+ if (menu_runner.RunMenuAt(source->GetWidget(), NULL,
+ gfx::Rect(pt, gfx::Size()), views::MenuItemView::TOPRIGHT,
+ views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
+ return;
}
diff --git a/chrome/browser/ui/views/infobars/extension_infobar.cc b/chrome/browser/ui/views/infobars/extension_infobar.cc
index 8372982..ad83e0f 100644
--- a/chrome/browser/ui/views/infobars/extension_infobar.cc
+++ b/chrome/browser/ui/views/infobars/extension_infobar.cc
@@ -19,6 +19,7 @@
#include "views/controls/button/menu_button.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
// ExtensionInfoBarDelegate ----------------------------------------------------
@@ -148,14 +149,14 @@ void ExtensionInfoBar::RunMenu(View* source, const gfx::Point& pt) {
scoped_refptr<ExtensionContextMenuModel> options_menu_contents =
new ExtensionContextMenuModel(extension, browser, NULL);
views::MenuModelAdapter options_menu_delegate(options_menu_contents.get());
- views::MenuItemView options_menu(&options_menu_delegate);
- options_menu_delegate.BuildMenu(&options_menu);
+ views::MenuRunner options_menu_runner(options_menu_delegate.CreateMenu());
gfx::Point screen_point;
views::View::ConvertPointToScreen(menu_, &screen_point);
- options_menu.RunMenuAt(GetWidget(), menu_,
- gfx::Rect(screen_point, menu_->size()), views::MenuItemView::TOPLEFT,
- true);
+ if (options_menu_runner.RunMenuAt(GetWidget(), menu_,
+ gfx::Rect(screen_point, menu_->size()), views::MenuItemView::TOPLEFT,
+ views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
+ return;
}
ExtensionInfoBarDelegate* ExtensionInfoBar::GetDelegate() {
diff --git a/chrome/browser/ui/views/location_bar/page_action_image_view.cc b/chrome/browser/ui/views/location_bar/page_action_image_view.cc
index 694428f..dc7086c 100644
--- a/chrome/browser/ui/views/location_bar/page_action_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/page_action_image_view.cc
@@ -17,6 +17,7 @@
#include "ui/base/accessibility/accessible_view_state.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
PageActionImageView::PageActionImageView(LocationBarView* owner,
ExtensionAction* page_action)
@@ -143,13 +144,14 @@ void PageActionImageView::ShowContextMenu(const gfx::Point& p,
scoped_refptr<ExtensionContextMenuModel> context_menu_model(
new ExtensionContextMenuModel(extension, owner_->browser(), this));
views::MenuModelAdapter menu_model_adapter(context_menu_model.get());
- views::MenuItemView menu(&menu_model_adapter);
- menu_model_adapter.BuildMenu(&menu);
+ views::MenuRunner menu_runner(menu_model_adapter.CreateMenu());
gfx::Point screen_loc;
views::View::ConvertPointToScreen(this, &screen_loc);
- menu.RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()),
- views::MenuItemView::TOPLEFT, true);
+ if (menu_runner.RunMenuAt(GetWidget(), NULL, gfx::Rect(screen_loc, size()),
+ views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
void PageActionImageView::OnImageLoaded(
diff --git a/chrome/browser/ui/views/menu_item_view_test.cc b/chrome/browser/ui/views/menu_item_view_test.cc
index a30769f..ab23e5d 100644
--- a/chrome/browser/ui/views/menu_item_view_test.cc
+++ b/chrome/browser/ui/views/menu_item_view_test.cc
@@ -7,6 +7,7 @@
#include "views/controls/button/menu_button.h"
#include "views/controls/menu/menu_controller.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/controls/menu/view_menu_delegate.h"
#include "views/widget/root_view.h"
@@ -30,10 +31,10 @@ class MenuItemViewTestBase : public ViewEventTestBase,
public views::ViewMenuDelegate,
public views::MenuDelegate {
public:
- MenuItemViewTestBase() :
- ViewEventTestBase(),
- button_(NULL),
- menu_(NULL) {
+ MenuItemViewTestBase()
+ : ViewEventTestBase(),
+ button_(NULL),
+ menu_(NULL) {
}
virtual ~MenuItemViewTestBase() {
@@ -43,14 +44,16 @@ class MenuItemViewTestBase : public ViewEventTestBase,
virtual void SetUp() OVERRIDE {
button_ = new views::MenuButton(NULL, L"Menu Test", this, true);
- menu_.reset(new views::MenuItemView(this));
- BuildMenu(menu_.get());
+ menu_ = new views::MenuItemView(this);
+ BuildMenu(menu_);
+ menu_runner_.reset(new views::MenuRunner(menu_));
ViewEventTestBase::SetUp();
}
virtual void TearDown() OVERRIDE {
- menu_.reset(NULL);
+ menu_runner_.reset(NULL);
+ menu_ = NULL;
ViewEventTestBase::TearDown();
}
@@ -67,12 +70,12 @@ class MenuItemViewTestBase : public ViewEventTestBase,
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- menu_->RunMenuAt(
+ ignore_result(menu_runner_->RunMenuAt(
source->GetWidget(),
button_,
bounds,
views::MenuItemView::TOPLEFT,
- true);
+ views::MenuRunner::HAS_MNEMONICS));
}
protected:
@@ -89,7 +92,8 @@ class MenuItemViewTestBase : public ViewEventTestBase,
}
views::MenuButton* button_;
- scoped_ptr<views::MenuItemView> menu_;
+ views::MenuItemView* menu_;
+ scoped_ptr<views::MenuRunner> menu_runner_;
};
// Simple test for clicking a menu item. This template class clicks on an
@@ -125,7 +129,7 @@ class MenuItemViewTestBasic : public MenuItemViewTestBase {
// Click on item INDEX.
void Step1() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -187,7 +191,7 @@ class MenuItemViewTestInsert : public MenuItemViewTestBase {
// Insert item at INSERT_INDEX and click item at SELECT_INDEX.
void Step1() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -208,7 +212,7 @@ class MenuItemViewTestInsert : public MenuItemViewTestBase {
// Check clicked item and complete test.
void Step2() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -318,8 +322,8 @@ VIEW_TEST(MenuItemViewTestInsertWithSubmenu1, InsertItemWithSubmenu1)
template<int REMOVE_INDEX, int SELECT_INDEX>
class MenuItemViewTestRemove : public MenuItemViewTestBase {
public:
- MenuItemViewTestRemove() :
- last_command_(0) {
+ MenuItemViewTestRemove()
+ : last_command_(0) {
}
virtual ~MenuItemViewTestRemove() {
@@ -344,7 +348,7 @@ class MenuItemViewTestRemove : public MenuItemViewTestBase {
// Remove item at REMOVE_INDEX and click item at SELECT_INDEX.
void Step1() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -363,7 +367,7 @@ class MenuItemViewTestRemove : public MenuItemViewTestBase {
// Check clicked item and complete test.
void Step2() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -428,7 +432,7 @@ class MenuItemViewTestRemoveWithSubmenu : public MenuItemViewTestBase {
// Post submenu.
void Step1() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -440,7 +444,7 @@ class MenuItemViewTestRemoveWithSubmenu : public MenuItemViewTestBase {
// Remove item at REMOVE_INDEX and select it to exit the menu loop.
void Step2() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
@@ -457,7 +461,7 @@ class MenuItemViewTestRemoveWithSubmenu : public MenuItemViewTestBase {
}
void Step3() {
- ASSERT_TRUE(menu_.get());
+ ASSERT_TRUE(menu_);
views::SubmenuView* submenu = menu_->GetSubmenu();
ASSERT_TRUE(submenu);
diff --git a/chrome/browser/ui/views/menu_model_adapter_test.cc b/chrome/browser/ui/views/menu_model_adapter_test.cc
index 85466d5..a13e165 100644
--- a/chrome/browser/ui/views/menu_model_adapter_test.cc
+++ b/chrome/browser/ui/views/menu_model_adapter_test.cc
@@ -9,6 +9,7 @@
#include "views/controls/menu/menu_controller.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/controls/menu/view_menu_delegate.h"
#include "views/test/test_views_delegate.h"
@@ -247,10 +248,11 @@ class TopMenuModel : public CommonMenuModel {
class MenuModelAdapterTest : public ViewEventTestBase,
public views::ViewMenuDelegate {
public:
- MenuModelAdapterTest() :
- ViewEventTestBase(),
- button_(NULL),
- menu_model_adapter_(&top_menu_model_) {
+ MenuModelAdapterTest()
+ : ViewEventTestBase(),
+ button_(NULL),
+ menu_model_adapter_(&top_menu_model_),
+ menu_(NULL) {
old_views_delegate_ = views::ViewsDelegate::views_delegate;
views::ViewsDelegate::views_delegate = &views_delegate_;
}
@@ -264,14 +266,15 @@ class MenuModelAdapterTest : public ViewEventTestBase,
virtual void SetUp() OVERRIDE {
button_ = new views::MenuButton(NULL, L"Menu Adapter Test", this, true);
- menu_.reset(new views::MenuItemView(&menu_model_adapter_));
- menu_model_adapter_.BuildMenu(menu_.get());
+ menu_ = menu_model_adapter_.CreateMenu();
+ menu_runner_.reset(new views::MenuRunner(menu_));
ViewEventTestBase::SetUp();
}
virtual void TearDown() OVERRIDE {
- menu_.reset(NULL);
+ menu_runner_.reset(NULL);
+ menu_ = NULL;
ViewEventTestBase::TearDown();
}
@@ -288,12 +291,12 @@ class MenuModelAdapterTest : public ViewEventTestBase,
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
- menu_->RunMenuAt(
+ ignore_result(menu_runner_->RunMenuAt(
source->GetWidget(),
button_,
bounds,
views::MenuItemView::TOPLEFT,
- true);
+ views::MenuRunner::HAS_MNEMONICS));
}
// ViewEventTestBase implementation
@@ -321,7 +324,7 @@ class MenuModelAdapterTest : public ViewEventTestBase,
ASSERT_TRUE(topmenu->IsShowing());
ASSERT_TRUE(top_menu_model_.IsSubmenuShowing());
- menu_model_adapter_.BuildMenu(menu_.get());
+ menu_model_adapter_.BuildMenu(menu_);
MessageLoopForUI::current()->PostTask(
FROM_HERE,
@@ -366,7 +369,8 @@ class MenuModelAdapterTest : public ViewEventTestBase,
views::MenuButton* button_;
TopMenuModel top_menu_model_;
views::MenuModelAdapter menu_model_adapter_;
- scoped_ptr<views::MenuItemView> menu_;
+ views::MenuItemView* menu_;
+ scoped_ptr<views::MenuRunner> menu_runner_;
};
VIEW_TEST(MenuModelAdapterTest, RebuildMenu)
diff --git a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
index 41194b4..cdf0ba9 100644
--- a/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
+++ b/chrome/browser/ui/views/tab_contents/render_view_context_menu_views.cc
@@ -48,9 +48,11 @@ void RenderViewContextMenuViews::RunMenuAt(int x, int y) {
static_cast<TabContentsViewViews*>(source_tab_contents_->view());
views::Widget* parent = tab->GetTopLevelWidget();
#endif
- menu_runner_->RunMenuAt(parent, NULL,
- gfx::Rect(gfx::Point(x, y), gfx::Size()),
- views::MenuItemView::TOPLEFT, true);
+ if (menu_runner_->RunMenuAt(parent, NULL,
+ gfx::Rect(gfx::Point(x, y), gfx::Size()),
+ views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
#if defined(OS_WIN)
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 7bca147..1a9ab98 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -24,6 +24,7 @@
#include "content/common/notification_service.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
static TabRendererData::NetworkState TabContentsNetworkState(
@@ -40,19 +41,18 @@ class BrowserTabStripController::TabContextMenuContents
public:
TabContextMenuContents(BaseTab* tab,
BrowserTabStripController* controller)
- : ALLOW_THIS_IN_INITIALIZER_LIST(
- model_(this,
- controller->model_,
- controller->tabstrip_->GetModelIndexOfBaseTab(tab))),
- menu_model_adapter_(&model_),
- menu_(&menu_model_adapter_),
- tab_(tab),
+ : tab_(tab),
controller_(controller),
last_command_(TabStripModel::CommandFirst) {
- menu_model_adapter_.BuildMenu(&menu_);
+ model_.reset(new TabMenuModel(
+ this, controller->model_,
+ controller->tabstrip_->GetModelIndexOfBaseTab(tab)));
+ menu_model_adapter_.reset(new views::MenuModelAdapter(model_.get()));
+ menu_runner_.reset(
+ new views::MenuRunner(menu_model_adapter_->CreateMenu()));
}
+
virtual ~TabContextMenuContents() {
- menu_.Cancel();
if (controller_)
controller_->tabstrip_->StopAllHighlighting();
}
@@ -62,9 +62,11 @@ class BrowserTabStripController::TabContextMenuContents
}
void RunMenuAt(const gfx::Point& point) {
- menu_.RunMenuAt(tab_->GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
- views::MenuItemView::TOPLEFT, true);
- // We could be gone now. Assume |this| is junk!
+ if (menu_runner_->RunMenuAt(
+ tab_->GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
+ views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
}
// Overridden from ui::SimpleMenuModel::Delegate:
@@ -109,9 +111,9 @@ class BrowserTabStripController::TabContextMenuContents
}
private:
- TabMenuModel model_;
- views::MenuModelAdapter menu_model_adapter_;
- views::MenuItemView menu_;
+ scoped_ptr<TabMenuModel> model_;
+ scoped_ptr<views::MenuModelAdapter> menu_model_adapter_;
+ scoped_ptr<views::MenuRunner> menu_runner_;
// The tab we're showing a menu for.
BaseTab* tab_;
diff --git a/chrome/browser/ui/views/toolbar_view.cc b/chrome/browser/ui/views/toolbar_view.cc
index 1452c96..337635e 100644
--- a/chrome/browser/ui/views/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar_view.cc
@@ -328,7 +328,7 @@ bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) {
void ToolbarView::RunMenu(views::View* source, const gfx::Point& /* pt */) {
DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
- wrench_menu_ = new WrenchMenu(browser_);
+ wrench_menu_.reset(new WrenchMenu(browser_));
wrench_menu_->Init(wrench_menu_model_.get());
for (size_t i = 0; i < menu_listeners_.size(); ++i)
diff --git a/chrome/browser/ui/views/toolbar_view.h b/chrome/browser/ui/views/toolbar_view.h
index fd0a83c..f24c9fb 100644
--- a/chrome/browser/ui/views/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar_view.h
@@ -9,7 +9,6 @@
#include <set>
#include <vector>
-#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/prefs/pref_member.h"
@@ -202,7 +201,7 @@ class ToolbarView : public AccessiblePaneView,
scoped_ptr<ui::SimpleMenuModel> wrench_menu_model_;
// Wrench menu.
- scoped_refptr<WrenchMenu> wrench_menu_;
+ scoped_ptr<WrenchMenu> wrench_menu_;
// Vector of listeners to receive callbacks when the menu opens.
std::vector<views::MenuListener*> menu_listeners_;
diff --git a/chrome/browser/ui/views/wrench_menu.cc b/chrome/browser/ui/views/wrench_menu.cc
index 386e8d5..6fb7950 100644
--- a/chrome/browser/ui/views/wrench_menu.cc
+++ b/chrome/browser/ui/views/wrench_menu.cc
@@ -36,6 +36,7 @@
#include "views/controls/label.h"
#include "views/controls/menu/menu_config.h"
#include "views/controls/menu/menu_item_view.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/menu_scroll_view_container.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/widget.h"
@@ -565,35 +566,42 @@ class WrenchMenu::ZoomView : public WrenchMenuView,
// WrenchMenu ------------------------------------------------------------------
WrenchMenu::WrenchMenu(Browser* browser)
- : browser_(browser),
+ : root_(NULL),
+ browser_(browser),
selected_menu_model_(NULL),
selected_index_(0),
bookmark_menu_(NULL),
first_bookmark_command_id_(0) {
}
+WrenchMenu::~WrenchMenu() {
+ if (bookmark_menu_delegate_.get()) {
+ BookmarkModel* model = browser_->profile()->GetBookmarkModel();
+ if (model)
+ model->RemoveObserver(this);
+ }
+}
+
void WrenchMenu::Init(ui::MenuModel* model) {
- DCHECK(!root_.get());
- root_.reset(new MenuItemView(this));
+ DCHECK(!root_);
+ root_ = new MenuItemView(this);
root_->set_has_icons(true); // We have checks, radios and icons, set this
// so we get the taller menu style.
int next_id = 1;
- PopulateMenu(root_.get(), model, &next_id);
+ PopulateMenu(root_, model, &next_id);
first_bookmark_command_id_ = next_id + 1;
+ menu_runner_.reset(new views::MenuRunner(root_));
}
void WrenchMenu::RunMenu(views::MenuButton* host) {
- // Up the ref count while the menu is displaying. This way if the window is
- // deleted while we're running we won't prematurely delete the menu.
- // TODO(sky): fix this, the menu should really take ownership of the menu
- // (57890).
- scoped_refptr<WrenchMenu> dont_delete_while_running(this);
gfx::Point screen_loc;
views::View::ConvertPointToScreen(host, &screen_loc);
gfx::Rect bounds(screen_loc, host->size());
UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu"));
- root_->RunMenuAt(host->GetWidget(), host, bounds,
- MenuItemView::TOPRIGHT, true);
+ if (menu_runner_->RunMenuAt(host->GetWidget(), host, bounds,
+ MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
+ views::MenuRunner::MENU_DELETED)
+ return;
if (bookmark_menu_delegate_.get()) {
BookmarkModel* model = browser_->profile()->GetBookmarkModel();
if (model)
@@ -765,9 +773,6 @@ void WrenchMenu::BookmarkModelChanged() {
root_->Cancel();
}
-WrenchMenu::~WrenchMenu() {
-}
-
void WrenchMenu::PopulateMenu(MenuItemView* parent,
MenuModel* model,
int* next_id) {
diff --git a/chrome/browser/ui/views/wrench_menu.h b/chrome/browser/ui/views/wrench_menu.h
index 76c9885..c18c5f0 100644
--- a/chrome/browser/ui/views/wrench_menu.h
+++ b/chrome/browser/ui/views/wrench_menu.h
@@ -9,7 +9,6 @@
#include <map>
#include <utility>
-#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/bookmarks/base_bookmark_model_observer.h"
#include "ui/base/models/menu_model.h"
@@ -21,15 +20,16 @@ class Browser;
namespace views {
class MenuButton;
class MenuItemView;
+class MenuRunner;
class View;
} // namespace views
// WrenchMenu adapts the WrenchMenuModel to view's menu related classes.
-class WrenchMenu : public base::RefCounted<WrenchMenu>,
- public views::MenuDelegate,
+class WrenchMenu : public views::MenuDelegate,
public BaseBookmarkModelObserver {
public:
explicit WrenchMenu(Browser* browser);
+ virtual ~WrenchMenu();
void Init(ui::MenuModel* model);
@@ -72,16 +72,12 @@ class WrenchMenu : public base::RefCounted<WrenchMenu>,
virtual void BookmarkModelChanged() OVERRIDE;
private:
- friend class base::RefCounted<WrenchMenu>;
-
class CutCopyPasteView;
class ZoomView;
typedef std::pair<ui::MenuModel*,int> Entry;
typedef std::map<int,Entry> IDToEntry;
- virtual ~WrenchMenu();
-
// Populates |parent| with all the child menus in |model|. Recursively invokes
// |PopulateMenu| for any submenu. |next_id| is incremented for every menu
// that is created.
@@ -110,8 +106,10 @@ class WrenchMenu : public base::RefCounted<WrenchMenu>,
return bookmark_menu_delegate_.get() && id >= first_bookmark_command_id_;
}
- // The views menu.
- scoped_ptr<views::MenuItemView> root_;
+ // The views menu. Owned by |menu_runner_|.
+ views::MenuItemView* root_;
+
+ scoped_ptr<views::MenuRunner> menu_runner_;
// Maps from the ID as understood by MenuItemView to the model/index pair the
// item came from.
diff --git a/views/controls/button/button_dropdown.cc b/views/controls/button/button_dropdown.cc
index 16c0313..44b79d3 100644
--- a/views/controls/button/button_dropdown.cc
+++ b/views/controls/button/button_dropdown.cc
@@ -13,6 +13,7 @@
#include "ui/base/models/menu_model.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/widget/widget.h"
namespace views {
@@ -155,20 +156,21 @@ void ButtonDropDown::ShowDropDownMenu(gfx::NativeView window) {
if (model_) {
MenuModelAdapter menu_delegate(model_);
menu_delegate.set_triggerable_event_flags(triggerable_event_flags());
- MenuItemView menu(&menu_delegate);
- menu_delegate.BuildMenu(&menu);
-
- menu.RunMenuAt(GetWidget(), NULL,
- gfx::Rect(menu_position, gfx::Size(0, 0)),
- views::MenuItemView::TOPLEFT,
- true);
+ MenuRunner runner(menu_delegate.CreateMenu());
+ if (runner.RunMenuAt(GetWidget(), NULL,
+ gfx::Rect(menu_position, gfx::Size(0, 0)),
+ MenuItemView::TOPLEFT,
+ MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED)
+ return;
} else {
MenuDelegate menu_delegate;
- MenuItemView menu(&menu_delegate);
- menu.RunMenuAt(GetWidget(), NULL,
- gfx::Rect(menu_position, gfx::Size(0, 0)),
- views::MenuItemView::TOPLEFT,
- true);
+ MenuItemView* menu = new MenuItemView(&menu_delegate);
+ MenuRunner runner(menu);
+ if (runner.RunMenuAt(GetWidget(), NULL,
+ gfx::Rect(menu_position, gfx::Size(0, 0)),
+ views::MenuItemView::TOPLEFT,
+ MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED)
+ return;
}
// Need to explicitly clear mouse handler so that events get sent
diff --git a/views/controls/combobox/native_combobox_views.cc b/views/controls/combobox/native_combobox_views.cc
index 6a7b318..4696615 100644
--- a/views/controls/combobox/native_combobox_views.cc
+++ b/views/controls/combobox/native_combobox_views.cc
@@ -18,6 +18,7 @@
#include "views/border.h"
#include "views/controls/combobox/combobox.h"
#include "views/controls/focusable_border.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/widget/root_view.h"
#include "views/widget/widget.h"
@@ -163,7 +164,9 @@ void NativeComboboxViews::UpdateFromModel() {
int max_width = 0;
const gfx::Font &font = GetFont();
- dropdown_list_menu_.reset(new MenuItemView(this));
+ MenuItemView* menu = new MenuItemView(this);
+ // MenuRunner owns |menu|.
+ dropdown_list_menu_runner_.reset(new MenuRunner(menu));
int num_items = combobox_->model()->GetItemCount();
for (int i = 0; i < num_items; ++i) {
@@ -173,10 +176,10 @@ void NativeComboboxViews::UpdateFromModel() {
// text is displayed correctly in right-to-left UIs.
base::i18n::AdjustStringForLocaleDirection(&text);
- dropdown_list_menu_->AppendMenuItem(i + kFirstMenuItemId, UTF16ToWide(text),
- MenuItemView::NORMAL);
- max_width = std::max(max_width, font.GetStringWidth(text));
- }
+ menu->AppendMenuItem(i + kFirstMenuItemId, UTF16ToWide(text),
+ MenuItemView::NORMAL);
+ max_width = std::max(max_width, font.GetStringWidth(text));
+ }
content_width_ = max_width;
content_height_ = font.GetFontSize();
@@ -342,11 +345,11 @@ void NativeComboboxViews::PaintText(gfx::Canvas* canvas) {
void NativeComboboxViews::ShowDropDownMenu() {
- if (!dropdown_list_menu_.get())
+ if (!dropdown_list_menu_runner_.get())
UpdateFromModel();
// Extend the menu to the width of the combobox.
- SubmenuView* submenu = dropdown_list_menu_->CreateSubmenu();
+ SubmenuView* submenu = dropdown_list_menu_runner_->GetMenu()->CreateSubmenu();
submenu->set_minimum_preferred_width(size().width());
gfx::Rect lb = GetLocalBounds();
@@ -358,8 +361,10 @@ void NativeComboboxViews::ShowDropDownMenu() {
gfx::Rect bounds(menu_position, lb.size());
dropdown_open_ = true;
- dropdown_list_menu_->RunMenuAt(NULL, NULL, bounds, MenuItemView::TOPLEFT,
- true);
+ if (dropdown_list_menu_runner_->RunMenuAt(
+ NULL, NULL, bounds, MenuItemView::TOPLEFT,
+ MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED)
+ return;
dropdown_open_ = false;
// Need to explicitly clear mouse handler so that events get sent
diff --git a/views/controls/combobox/native_combobox_views.h b/views/controls/combobox/native_combobox_views.h
index f4cb56b..5722e96 100644
--- a/views/controls/combobox/native_combobox_views.h
+++ b/views/controls/combobox/native_combobox_views.h
@@ -19,6 +19,7 @@ namespace views {
class KeyEvent;
class FocusableBorder;
+class MenuRunner;
// A views/skia only implementation of NativeComboboxWrapper.
// No platform specific code is used.
@@ -82,8 +83,8 @@ class NativeComboboxViews : public views::View,
// The reference to the border class. The object is owned by View::border_.
FocusableBorder* text_border_;
- // Context menu and its content list for the combobox.
- scoped_ptr<views::MenuItemView> dropdown_list_menu_;
+ // Responsible for showing the context menu.
+ scoped_ptr<MenuRunner> dropdown_list_menu_runner_;
// Is the drop down list showing
bool dropdown_open_;
diff --git a/views/controls/menu/menu_controller.cc b/views/controls/menu/menu_controller.cc
index c44aa9a..cb53602 100644
--- a/views/controls/menu/menu_controller.cc
+++ b/views/controls/menu/menu_controller.cc
@@ -14,6 +14,7 @@
#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/screen.h"
#include "views/controls/button/menu_button.h"
+#include "views/controls/menu/menu_controller_delegate.h"
#include "views/controls/menu/menu_scroll_view_container.h"
#include "views/controls/menu/submenu_view.h"
#include "views/drag_utils.h"
@@ -369,7 +370,7 @@ void MenuController::Cancel(ExitType type) {
// If the menu has already been destroyed, no further cancellation is
// needed. We especially don't want to set the |exit_type_| to a lesser
// value.
- if (exit_type_ == EXIT_DESTROYED)
+ if (exit_type_ == EXIT_DESTROYED || exit_type_ == type)
return;
if (!showing_) {
@@ -391,7 +392,9 @@ void MenuController::Cancel(ExitType type) {
// triggers deleting us.
DCHECK(selected);
showing_ = false;
- selected->GetRootMenuItem()->DropMenuClosed(true);
+ delegate_->DropMenuClosed(
+ internal::MenuControllerDelegate::NOTIFY_DELEGATE,
+ selected->GetRootMenuItem());
// WARNING: the call to MenuClosed deletes us.
return;
}
@@ -716,8 +719,11 @@ int MenuController::OnPerformDrop(SubmenuView* source,
if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID)
drop_target = drop_target->GetParentMenuItem();
- if (!IsBlockingRun())
- item->GetRootMenuItem()->DropMenuClosed(false);
+ if (!IsBlockingRun()) {
+ delegate_->DropMenuClosed(
+ internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE,
+ item->GetRootMenuItem());
+ }
// WARNING: the call to MenuClosed deletes us.
@@ -808,11 +814,6 @@ void MenuController::SetSelection(MenuItemView* menu_item,
}
}
-// static
-void MenuController::SetActiveInstance(MenuController* controller) {
- active_instance_ = controller;
-}
-
#if defined(OS_WIN)
bool MenuController::Dispatch(const MSG& msg) {
DCHECK(blocking_run_);
@@ -994,7 +995,8 @@ bool MenuController::OnKeyDown(int key_code
return true;
}
-MenuController::MenuController(bool blocking)
+MenuController::MenuController(bool blocking,
+ internal::MenuControllerDelegate* delegate)
: blocking_run_(blocking),
showing_(false),
exit_type_(EXIT_NONE),
@@ -1008,11 +1010,15 @@ MenuController::MenuController(bool blocking)
valid_drop_coordinates_(false),
showing_submenu_(false),
menu_button_(NULL),
- active_mouse_view_(NULL) {
+ active_mouse_view_(NULL),
+ delegate_(delegate) {
+ active_instance_ = this;
}
MenuController::~MenuController() {
DCHECK(!showing_);
+ if (active_instance_ == this)
+ active_instance_ = NULL;
StopShowTimer();
StopCancelAllTimer();
}
@@ -1095,6 +1101,8 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source,
if (!alt_menu || (state_.item && state_.item->GetRootMenuItem() == alt_menu))
return false;
+ delegate_->SiblingMenuCreated(alt_menu);
+
if (!button) {
// If the delegate returns a menu, they must also return a button.
NOTREACHED();
diff --git a/views/controls/menu/menu_controller.h b/views/controls/menu/menu_controller.h
index cf2fa49..35b7a78 100644
--- a/views/controls/menu/menu_controller.h
+++ b/views/controls/menu/menu_controller.h
@@ -32,6 +32,11 @@ class MouseEvent;
class SubmenuView;
class View;
+namespace internal {
+class MenuControllerDelegate;
+class MenuRunnerImpl;
+}
+
// MenuController -------------------------------------------------------------
// MenuController is used internally by the various menu classes to manage
@@ -39,10 +44,6 @@ class View;
// forwarded to the MenuController from SubmenuView and MenuHost.
class VIEWS_EXPORT MenuController : public MessageLoop::Dispatcher {
public:
- friend class MenuHostRootView;
- friend class MenuItemView;
- friend class SubmenuView;
-
// Enumeration of how the menu should exit.
enum ExitType {
// Don't exit.
@@ -122,6 +123,11 @@ class VIEWS_EXPORT MenuController : public MessageLoop::Dispatcher {
void OnWidgetActivationChanged();
private:
+ friend class internal::MenuRunnerImpl;
+ friend class MenuHostRootView;
+ friend class MenuItemView;
+ friend class SubmenuView;
+
class MenuScrollTask;
struct SelectByCharDetails;
@@ -209,9 +215,6 @@ class VIEWS_EXPORT MenuController : public MessageLoop::Dispatcher {
// to show/hide submenus and update state_.
void SetSelection(MenuItemView* menu_item, int types);
- // 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.
@@ -231,8 +234,9 @@ class VIEWS_EXPORT MenuController : public MessageLoop::Dispatcher {
bool OnKeyDown(int key_code);
#endif
- // Creates a MenuController. If blocking is true, Run blocks the caller
- explicit MenuController(bool blocking);
+ // Creates a MenuController. If |blocking| is true a nested message loop is
+ // started in |Run|.
+ MenuController(bool blocking, internal::MenuControllerDelegate* delegate);
virtual ~MenuController();
@@ -494,6 +498,8 @@ class VIEWS_EXPORT MenuController : public MessageLoop::Dispatcher {
// UpdateActiveMouseView for details.
View* active_mouse_view_;
+ internal::MenuControllerDelegate* delegate_;
+
DISALLOW_COPY_AND_ASSIGN(MenuController);
};
diff --git a/views/controls/menu/menu_controller_delegate.h b/views/controls/menu/menu_controller_delegate.h
new file mode 100644
index 0000000..db68579
--- /dev/null
+++ b/views/controls/menu/menu_controller_delegate.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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_CONTROLLER_DELEGATE_H_
+#define VIEWS_CONTROLS_MENU_MENU_CONTROLLER_DELEGATE_H_
+#pragma once
+
+namespace views {
+
+class MenuItemView;
+
+// This is internal as there should be no need for usage of this class outside
+// of views.
+namespace internal {
+
+// Used by MenuController to notify of interesting events that are intended for
+// the class using MenuController. This is implemented by MenuRunnerImpl.
+class MenuControllerDelegate {
+ public:
+ enum NotifyType {
+ NOTIFY_DELEGATE,
+ DONT_NOTIFY_DELEGATE
+ };
+
+ // Invoked when MenuController closes a menu and the MenuController was
+ // configured for drop (MenuRunner::FOR_DROP).
+ virtual void DropMenuClosed(NotifyType type, MenuItemView* menu) = 0;
+
+ // Invoked when the MenuDelegate::GetSiblingMenu() returns non-NULL.
+ virtual void SiblingMenuCreated(MenuItemView* menu) = 0;
+
+ protected:
+ virtual ~MenuControllerDelegate() {}
+};
+
+} // namespace internal
+
+} // namespace view
+
+#endif // VIEWS_CONTROLS_MENU_MENU_CONTROLLER_DELEGATE_H_
diff --git a/views/controls/menu/menu_item_view.cc b/views/controls/menu/menu_item_view.cc
index df4a2a8..8181793 100644
--- a/views/controls/menu/menu_item_view.cc
+++ b/views/controls/menu/menu_item_view.cc
@@ -19,10 +19,6 @@
#include "views/controls/menu/menu_separator.h"
#include "views/controls/menu/submenu_view.h"
-#if defined(OS_WIN)
-#include "base/win/win_util.h"
-#endif
-
namespace views {
namespace {
@@ -100,16 +96,6 @@ MenuItemView::MenuItemView(MenuDelegate* delegate)
Init(NULL, 0, SUBMENU, delegate);
}
-MenuItemView::~MenuItemView() {
- // TODO(sky): ownership is bit wrong now. In particular if a nested message
- // loop is running deletion can't be done, otherwise the stack gets
- // thoroughly screwed. The destructor should be made private, and
- // MenuController should be the only place handling deletion of the menu.
- // (57890).
- delete submenu_;
- STLDeleteElements(&removed_items_);
-}
-
void MenuItemView::ChildPreferredSizeChanged(View* child) {
pref_size_.SetSize(0, 0);
PreferredSizeChanged();
@@ -192,86 +178,6 @@ string16 MenuItemView::GetAccessibleNameForMenuItem(
return accessible_name;
}
-void MenuItemView::RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- AnchorPosition anchor,
- bool has_mnemonics) {
- // Show mnemonics if the button has focus or alt is pressed.
- bool show_mnemonics = button ? button->HasFocus() : false;
-#if defined(OS_WIN)
- // We don't currently need this on gtk as showing the menu gives focus to the
- // button first.
- if (!show_mnemonics)
- show_mnemonics = base::win::IsAltPressed();
-#endif
- PrepareForRun(has_mnemonics, show_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(MenuController::EXIT_ALL);
- 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, button, 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(Widget* parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor) {
- PrepareForRun(false, 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(MenuController::EXIT_ALL);
- }
-
- // 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, NULL, this, bounds, anchor, NULL);
-}
-
void MenuItemView::Cancel() {
if (controller_ && !canceled_) {
canceled_ = true;
@@ -558,6 +464,11 @@ MenuItemView::MenuItemView(MenuItemView* parent,
Init(parent, command, type, NULL);
}
+MenuItemView::~MenuItemView() {
+ delete submenu_;
+ STLDeleteElements(&removed_items_);
+}
+
std::string MenuItemView::GetClassName() const {
return kViewClassName;
}
@@ -612,23 +523,6 @@ void MenuItemView::Init(MenuItemView* parent,
SetEnabled(root_delegate->IsCommandEnabled(command));
}
-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, bool show_mnemonics) {
// Currently we only support showing the root.
DCHECK(!parent_menu_item_);
diff --git a/views/controls/menu/menu_item_view.h b/views/controls/menu/menu_item_view.h
index af8f0f0..6b91c8d 100644
--- a/views/controls/menu/menu_item_view.h
+++ b/views/controls/menu/menu_item_view.h
@@ -33,13 +33,16 @@ class MenuModel;
namespace views {
+namespace internal {
+class MenuRunnerImpl;
+}
+
class MenuButton;
+struct MenuConfig;
class MenuController;
class MenuDelegate;
class SubmenuView;
-struct MenuConfig;
-
// MenuItemView --------------------------------------------------------------
// MenuItemView represents a single menu item with a label and optional icon.
@@ -59,12 +62,8 @@ struct MenuConfig;
// focus from the hosting window child views do not actually get focus. Instead
// |SetHotTracked| is used as the user navigates around.
//
-// 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.
+// To show the menu use MenuRunner. See MenuRunner for details on how to run
+// (show) the menu as well as for details on the life time of the menu.
class VIEWS_EXPORT MenuItemView : public View {
public:
@@ -111,8 +110,6 @@ class VIEWS_EXPORT MenuItemView : public View {
// shown to the user, rather its use as the parent for all menu items.
explicit MenuItemView(MenuDelegate* delegate);
- virtual ~MenuItemView();
-
// Overridden from View:
virtual bool GetTooltipText(const gfx::Point& p,
std::wstring* tooltip) OVERRIDE;
@@ -130,20 +127,6 @@ class VIEWS_EXPORT MenuItemView : public View {
static string16 GetAccessibleNameForMenuItem(
const string16& item_text, const string16& accelerator_text);
- // 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 '&'. The anchor position is specified for non-RTL
- // languages; the opposite value will be used for RTL.
- void RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- AnchorPosition anchor,
- bool has_mnemonics);
- void RunMenuForDropAt(Widget* parent,
- const gfx::Rect& bounds,
- AnchorPosition anchor);
-
// Hides and cancels the menu. This does nothing if the menu is not open.
void Cancel();
@@ -288,6 +271,7 @@ class VIEWS_EXPORT MenuItemView : public View {
// Returns the delegate. This returns the delegate of the root menu item.
MenuDelegate* GetDelegate();
+ void set_delegate(MenuDelegate* delegate) { delegate_ = delegate; }
// Returns the root parent, or this if this has no parent.
MenuItemView* GetRootMenuItem();
@@ -334,11 +318,16 @@ class VIEWS_EXPORT MenuItemView : public View {
// Creates a MenuItemView. This is used by the various AddXXX methods.
MenuItemView(MenuItemView* parent, int command, Type type);
+ // MenuRunner owns MenuItemView and should be the only one deleting it.
+ virtual ~MenuItemView();
+
virtual void ChildPreferredSizeChanged(View* child) OVERRIDE;
virtual std::string GetClassName() const OVERRIDE;
private:
+ friend class internal::MenuRunnerImpl; // For access to ~MenuItemView.
+
// Calculates all sizes that we can from the OS.
//
// This is invoked prior to Running a menu.
@@ -350,10 +339,6 @@ class VIEWS_EXPORT MenuItemView : public View {
MenuItemView::Type type,
MenuDelegate* delegate);
- // 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, bool show_mnemonics);
@@ -418,13 +403,14 @@ class VIEWS_EXPORT MenuItemView : public View {
actual_menu_position_ = actual_menu_position;
}
+ void set_controller(MenuController* controller) { controller_ = controller; }
+
// 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.
+ // The controller for the run operation, or NULL if the menu isn't showing.
MenuController* controller_;
// Used to detect when Cancel was invoked.
diff --git a/views/controls/menu/menu_model_adapter.cc b/views/controls/menu/menu_model_adapter.cc
index e2c895d..850b6b5 100644
--- a/views/controls/menu/menu_model_adapter.cc
+++ b/views/controls/menu/menu_model_adapter.cc
@@ -44,6 +44,12 @@ void MenuModelAdapter::BuildMenu(MenuItemView* menu) {
menu->ChildrenChanged();
}
+MenuItemView* MenuModelAdapter::CreateMenu() {
+ MenuItemView* item = new MenuItemView(this);
+ BuildMenu(item);
+ return item;
+}
+
// MenuModelAdapter, MenuDelegate implementation:
void MenuModelAdapter::ExecuteCommand(int id) {
diff --git a/views/controls/menu/menu_model_adapter.h b/views/controls/menu/menu_model_adapter.h
index a1a90a2..ba50ef4 100644
--- a/views/controls/menu/menu_model_adapter.h
+++ b/views/controls/menu/menu_model_adapter.h
@@ -30,6 +30,10 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate {
// (including submenus).
virtual void BuildMenu(MenuItemView* menu);
+ // Convenience for creating and populating a menu. The caller owns the
+ // returned MenuItemView.
+ MenuItemView* CreateMenu();
+
void set_triggerable_event_flags(int triggerable_event_flags) {
triggerable_event_flags_ = triggerable_event_flags;
}
diff --git a/views/controls/menu/menu_model_adapter_unittest.cc b/views/controls/menu/menu_model_adapter_unittest.cc
index 035ec16..f797d40 100644
--- a/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/views/controls/menu/menu_model_adapter_unittest.cc
@@ -7,6 +7,7 @@
#include "ui/base/models/menu_model_delegate.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/menu/submenu_view.h"
#include "views/test/views_test_base.h"
@@ -200,9 +201,11 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
views::MenuModelAdapter delegate(&model);
// Create menu. Build menu twice to check that rebuilding works properly.
- scoped_ptr<views::MenuItemView> menu(new views::MenuItemView(&delegate));
- delegate.BuildMenu(menu.get());
- delegate.BuildMenu(menu.get());
+ MenuItemView* menu = new views::MenuItemView(&delegate);
+ // MenuRunner takes ownership of menu.
+ scoped_ptr<MenuRunner> menu_runner(new MenuRunner(menu));
+ delegate.BuildMenu(menu);
+ delegate.BuildMenu(menu);
EXPECT_TRUE(menu->HasSubmenu());
// Check top level menu items.
@@ -297,7 +300,7 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
// Check that selecting the root item is safe. The MenuModel does
// not care about the root so MenuModelAdapter should do nothing
// (not hit the NOTREACHED check) when the root is selected.
- static_cast<views::MenuDelegate*>(&delegate)->SelectionChanged(menu.get());
+ static_cast<views::MenuDelegate*>(&delegate)->SelectionChanged(menu);
}
} // namespace views
diff --git a/views/controls/menu/menu_runner.cc b/views/controls/menu/menu_runner.cc
index 1941d69a..89cb114 100644
--- a/views/controls/menu/menu_runner.cc
+++ b/views/controls/menu/menu_runner.cc
@@ -4,30 +4,69 @@
#include "views/controls/menu/menu_runner.h"
+#include <set>
+
+#include "views/controls/button/menu_button.h"
+#include "views/controls/menu/menu_controller.h"
+#include "views/controls/menu/menu_controller_delegate.h"
+#include "views/controls/menu/menu_delegate.h"
+
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
namespace views {
-// Manages the menu. To destroy a Holder invoke Release(). Release() deletes
-// immediately if the menu isn't showing. If the menu is showing Release()
-// cancels the menu and when the nested RunMenuAt() call returns, deletes itself
-// and the menu.
-class MenuRunner::Holder {
+namespace internal {
+
+// Manages the menu. To destroy a MenuRunnerImpl invoke Release(). Release()
+// deletes immediately if the menu isn't showing. If the menu is showing
+// Release() cancels the menu and when the nested RunMenuAt() call returns
+// deletes itself and the menu.
+class MenuRunnerImpl : public internal::MenuControllerDelegate {
public:
- explicit Holder(MenuItemView* menu);
+ explicit MenuRunnerImpl(MenuItemView* menu);
+
+ MenuItemView* menu() { return menu_; }
// See description above class for details.
void Release();
// Runs the menu.
- void RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition anchor,
- bool has_mnemonics);
+ MenuRunner::RunResult RunMenuAt(Widget* parent,
+ MenuButton* button,
+ const gfx::Rect& bounds,
+ MenuItemView::AnchorPosition anchor,
+ int32 types) WARN_UNUSED_RESULT;
+
+ void Cancel();
+
+ // MenuControllerDelegate:
+ virtual void DropMenuClosed(NotifyType type, MenuItemView* menu) OVERRIDE;
+ virtual void SiblingMenuCreated(MenuItemView* menu) OVERRIDE;
private:
- ~Holder();
+ ~MenuRunnerImpl();
+
+ // Cleans up after the menu is no longer showing. |result| is the menu that
+ // the user selected, or NULL if nothing was selected.
+ MenuRunner::RunResult MenuDone(MenuItemView* result, int mouse_event_flags);
+
+ // Returns true if mnemonics should be shown in the menu.
+ bool ShouldShowMnemonics(MenuButton* button);
+
+ // The menu. We own this. We don't use scoped_ptr as the destructor is
+ // protected and we're a friend.
+ MenuItemView* menu_;
+
+ // Any sibling menus. Does not include |menu_|. We own these too.
+ std::set<MenuItemView*> sibling_menus_;
- scoped_ptr<MenuItemView> menu_;
+ // Created and set as the delegate of the MenuItemView if Release() is
+ // invoked. This is done to make sure the delegate isn't notified after
+ // Release() is invoked. We do this as we assume the delegate is no longer
+ // valid if MenuRunner has been deleted.
+ scoped_ptr<MenuDelegate> empty_delegate_;
// Are we in run waiting for it to return?
bool running_;
@@ -35,63 +74,198 @@ class MenuRunner::Holder {
// Set if |running_| and Release() has been invoked.
bool delete_after_run_;
- DISALLOW_COPY_AND_ASSIGN(Holder);
+ // Are we running for a drop?
+ bool for_drop_;
+
+ // The controller.
+ MenuController* controller_;
+
+ // Do we own the controller?
+ bool owns_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(MenuRunnerImpl);
};
-MenuRunner::Holder::Holder(MenuItemView* menu)
+MenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu)
: menu_(menu),
running_(false),
- delete_after_run_(false) {
+ delete_after_run_(false),
+ for_drop_(false),
+ controller_(NULL),
+ owns_controller_(false) {
}
-void MenuRunner::Holder::Release() {
+void MenuRunnerImpl::Release() {
if (running_) {
+ if (delete_after_run_)
+ return; // We already canceled.
+
// The menu is running a nested message loop, we can't delete it now
// otherwise the stack would be in a really bad state (many frames would
// have deleted objects on them). Instead cancel the menu, when it returns
// Holder will delete itself.
delete_after_run_ = true;
+
+ // Swap in a different delegate. That way we know the original MenuDelegate
+ // won't be notified later on (when it's likely already been deleted).
+ if (!empty_delegate_.get())
+ empty_delegate_.reset(new MenuDelegate());
+ menu_->set_delegate(empty_delegate_.get());
+
menu_->Cancel();
} else {
delete this;
}
}
-void MenuRunner::Holder::RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition anchor,
- bool has_mnemonics) {
+MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(
+ Widget* parent,
+ MenuButton* button,
+ const gfx::Rect& bounds,
+ MenuItemView::AnchorPosition anchor,
+ int32 types) {
if (running_) {
// Ignore requests to show the menu while it's already showing. MenuItemView
// doesn't handle this very well (meaning it crashes).
- return;
+ return MenuRunner::NORMAL_EXIT;
+ }
+
+ MenuController* controller = MenuController::GetActiveInstance();
+ if (controller) {
+ if ((types & MenuRunner::IS_NESTED) != 0) {
+ if (!controller->IsBlockingRun()) {
+ controller->CancelAll();
+ controller = NULL;
+ }
+ } else {
+ // There's some other menu open and we're not nested. Cancel the menu.
+ controller->CancelAll();
+ if ((types & MenuRunner::FOR_DROP) == 0) {
+ // We can't open another menu, otherwise the message loop would become
+ // twice nested. This isn't necessarily a problem, but generally isn't
+ // expected.
+ return MenuRunner::NORMAL_EXIT;
+ }
+ // Drop menus don't block the message loop, so it's ok to create a new
+ // MenuController.
+ controller = NULL;
+ }
}
running_ = true;
- menu_->RunMenuAt(parent, button, bounds, anchor, has_mnemonics);
+ for_drop_ = (types & MenuRunner::FOR_DROP) != 0;
+ bool has_mnemonics = (types & MenuRunner::HAS_MNEMONICS) != 0 && !for_drop_;
+ menu_->PrepareForRun(has_mnemonics,
+ !for_drop_ && ShouldShowMnemonics(button));
+ int mouse_event_flags = 0;
+ owns_controller_ = false;
+ if (!controller) {
+ // No menus are showing, show one.
+ controller = new MenuController(!for_drop_, this);
+ owns_controller_ = true;
+ }
+ controller_ = controller;
+ menu_->set_controller(controller_);
+
+ // Run the loop.
+ MenuItemView* result = controller->Run(parent, button, menu_, bounds, anchor,
+ &mouse_event_flags);
+
+ if (for_drop_) {
+ // Drop menus return immediately. We finish processing in DropMenuClosed.
+ return MenuRunner::NORMAL_EXIT;
+ }
+
+ return MenuDone(result, mouse_event_flags);
+}
+
+void MenuRunnerImpl::Cancel() {
+ if (running_)
+ controller_->Cancel(MenuController::EXIT_ALL);
+}
+
+void MenuRunnerImpl::DropMenuClosed(NotifyType type, MenuItemView* menu) {
+ MenuDone(NULL, 0);
+
+ if (type == NOTIFY_DELEGATE && menu->GetDelegate()) {
+ // Delegate is null when invoked from the destructor.
+ menu->GetDelegate()->DropMenuClosed(menu);
+ }
+}
+
+void MenuRunnerImpl::SiblingMenuCreated(MenuItemView* menu) {
+ if (menu != menu_ && sibling_menus_.count(menu) == 0)
+ sibling_menus_.insert(menu);
+}
+
+MenuRunnerImpl::~MenuRunnerImpl() {
+ delete menu_;
+ for (std::set<MenuItemView*>::iterator i = sibling_menus_.begin();
+ i != sibling_menus_.end(); ++i)
+ delete *i;
+}
+
+MenuRunner::RunResult MenuRunnerImpl::MenuDone(MenuItemView* result,
+ int mouse_event_flags) {
+ menu_->RemoveEmptyMenus();
+ menu_->set_controller(NULL);
+
+ if (owns_controller_) {
+ // We created the controller and need to delete it.
+ delete controller_;
+ owns_controller_ = false;
+ }
+ controller_ = NULL;
+ // Make sure all the windows we created to show the menus have been
+ // destroyed.
+ menu_->DestroyAllMenuHosts();
if (delete_after_run_) {
delete this;
- return;
+ return MenuRunner::MENU_DELETED;
}
running_ = false;
+ if (result && menu_->GetDelegate()) {
+ menu_->GetDelegate()->ExecuteCommand(result->GetCommand(),
+ mouse_event_flags);
+ }
+ return MenuRunner::NORMAL_EXIT;
}
-MenuRunner::Holder::~Holder() {
+bool MenuRunnerImpl::ShouldShowMnemonics(MenuButton* button) {
+ // Show mnemonics if the button has focus or alt is pressed.
+ bool show_mnemonics = button ? button->HasFocus() : false;
+#if defined(OS_WIN)
+ // We don't currently need this on gtk as showing the menu gives focus to the
+ // button first.
+ if (!show_mnemonics)
+ show_mnemonics = base::win::IsAltPressed();
+#endif
+ return show_mnemonics;
}
-MenuRunner::MenuRunner(MenuItemView* menu) : holder_(new Holder(menu)) {
+} // namespace internal
+
+MenuRunner::MenuRunner(MenuItemView* menu)
+ : holder_(new internal::MenuRunnerImpl(menu)) {
}
MenuRunner::~MenuRunner() {
holder_->Release();
}
-void MenuRunner::RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition anchor,
- bool has_mnemonics) {
- holder_->RunMenuAt(parent, button, bounds, anchor, has_mnemonics);
+MenuItemView* MenuRunner::GetMenu() {
+ return holder_->menu();
+}
+
+MenuRunner::RunResult MenuRunner::RunMenuAt(Widget* parent,
+ MenuButton* button,
+ const gfx::Rect& bounds,
+ MenuItemView::AnchorPosition anchor,
+ int32 types) {
+ return holder_->RunMenuAt(parent, button, bounds, anchor, types);
+}
+
+void MenuRunner::Cancel() {
+ holder_->Cancel();
}
} // namespace views
diff --git a/views/controls/menu/menu_runner.h b/views/controls/menu/menu_runner.h
index 1337d08..5cdb845 100644
--- a/views/controls/menu/menu_runner.h
+++ b/views/controls/menu/menu_runner.h
@@ -7,6 +7,7 @@
#pragma once
#include "base/basictypes.h"
+#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "views/controls/menu/menu_item_view.h"
@@ -15,32 +16,73 @@ namespace views {
class MenuButton;
class Widget;
-// MenuRunner handles the lifetime of the root MenuItemView. MenuItemView runs a
-// nested message loop, which means care must be taken when the MenuItemView
-// needs to be deleted. MenuRunner makes sure the menu is deleted after the
-// nested message loop completes.
-//
-// MenuRunner can be deleted at any time and will correctly handle deleting the
-// underlying menu.
+namespace internal {
+class MenuRunnerImpl;
+}
+
+// MenuRunner is responsible for showing (running) the menu and additionally
+// owning the MenuItemView. RunMenuAt() runs a nested message loop. It is safe
+// to delete MenuRunner at any point, but MenuRunner internally only deletes the
+// MenuItemView *after* the nested message loop completes. If MenuRunner is
+// deleted while the menu is showing the delegate of the menu is reset. This is
+// done to ensure delegates aren't notified after they may have been deleted.
//
-// TODO: this is a work around for 57890. If we fix it this class shouldn't be
-// needed.
+// NOTE: while you can delete a MenuRunner at any point, the nested message loop
+// won't return immediately. This means if you delete the object that owns
+// the MenuRunner while the menu is running, your object is effectively still
+// on the stack. A return value of MENU_DELETED indicated this. In most cases
+// if RunMenuAt() returns MENU_DELETED, you should return immediately.
class VIEWS_EXPORT MenuRunner {
public:
+ enum RunTypes {
+ // The menu has mnemonics.
+ HAS_MNEMONICS = 1 << 0,
+
+ // The menu is a nested context menu. For example, click a folder on the
+ // bookmark bar, then right click an entry to get its context menu.
+ IS_NESTED = 1 << 1,
+
+ // Used for showing a menu during a drop operation. This does NOT block the
+ // caller, instead the delegate is notified when the menu closes via the
+ // DropMenuClosed method.
+ FOR_DROP = 1 << 2,
+ };
+
+ enum RunResult {
+ // Indicates RunMenuAt is returning because the MenuRunner was deleted.
+ MENU_DELETED,
+
+ // Indicates RunMenuAt returned and MenuRunner was not deleted.
+ NORMAL_EXIT
+ };
+
+ // Creates a new MenuRunner. MenuRunner owns the supplied menu.
explicit MenuRunner(MenuItemView* menu);
~MenuRunner();
- // Runs the menu.
- void RunMenuAt(Widget* parent,
- MenuButton* button,
- const gfx::Rect& bounds,
- MenuItemView::AnchorPosition anchor,
- bool has_mnemonics);
+ // Returns the menu.
+ MenuItemView* GetMenu();
- private:
- class Holder;
+ // Takes ownership of |menu|, deleting it when MenuRunner is deleted. You
+ // only need call this if you create additional menus from
+ // MenuDelegate::GetSiblingMenu.
+ void OwnMenu(MenuItemView* menu);
+
+ // Runs the menu. |types| is a bitmask of RunTypes. If this returns
+ // MENU_DELETED the method is returning because the MenuRunner was deleted.
+ // Typically callers should NOT do any processing if this returns
+ // MENU_DELETED.
+ RunResult RunMenuAt(Widget* parent,
+ MenuButton* button,
+ const gfx::Rect& bounds,
+ MenuItemView::AnchorPosition anchor,
+ int32 types) WARN_UNUSED_RESULT;
- Holder* holder_;
+ // Hides and cancels the menu. This does nothing if the menu is not open.
+ void Cancel();
+
+ private:
+ internal::MenuRunnerImpl* holder_;
DISALLOW_COPY_AND_ASSIGN(MenuRunner);
};
diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc
index 468a9c9..5b43215 100644
--- a/views/controls/textfield/native_textfield_views.cc
+++ b/views/controls/textfield/native_textfield_views.cc
@@ -22,6 +22,7 @@
#include "views/controls/focusable_border.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
+#include "views/controls/menu/menu_runner.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_controller.h"
#include "views/controls/textfield/textfield_views_model.h"
@@ -298,11 +299,11 @@ void NativeTextfieldViews::ShowContextMenuForView(View* source,
const gfx::Point& p,
bool is_mouse_gesture) {
UpdateContextMenu();
- context_menu_menu_->RunMenuAt(GetWidget(),
- NULL,
- gfx::Rect(p, gfx::Size()),
- views::MenuItemView::TOPLEFT,
- true);
+ if (context_menu_runner_->RunMenuAt(
+ GetWidget(), NULL, gfx::Rect(p, gfx::Size()),
+ views::MenuItemView::TOPLEFT, MenuRunner::HAS_MNEMONICS) ==
+ MenuRunner::MENU_DELETED)
+ return;
}
/////////////////////////////////////////////////////////////////
@@ -975,11 +976,11 @@ void NativeTextfieldViews::UpdateContextMenu() {
context_menu_delegate_.reset(
new views::MenuModelAdapter(context_menu_contents_.get()));
- context_menu_menu_.reset(
- new views::MenuItemView(context_menu_delegate_.get()));
+ context_menu_runner_.reset(
+ new MenuRunner(new views::MenuItemView(context_menu_delegate_.get())));
}
- context_menu_delegate_->BuildMenu(context_menu_menu_.get());
+ context_menu_delegate_->BuildMenu(context_menu_runner_->GetMenu());
}
void NativeTextfieldViews::OnTextInputTypeChanged() {
diff --git a/views/controls/textfield/native_textfield_views.h b/views/controls/textfield/native_textfield_views.h
index 1e26eb2..8849a3b 100644
--- a/views/controls/textfield/native_textfield_views.h
+++ b/views/controls/textfield/native_textfield_views.h
@@ -31,8 +31,8 @@ namespace views {
class FocusableBorder;
class KeyEvent;
-class MenuItemView;
class MenuModelAdapter;
+class MenuRunner;
// A views/skia only implementation of NativeTextfieldWrapper.
// No platform specific code is used.
@@ -249,7 +249,7 @@ class VIEWS_EXPORT NativeTextfieldViews : public TouchSelectionClientView,
// Context menu and its content list for the textfield.
scoped_ptr<ui::SimpleMenuModel> context_menu_contents_;
scoped_ptr<views::MenuModelAdapter> context_menu_delegate_;
- scoped_ptr<views::MenuItemView> context_menu_menu_;
+ scoped_ptr<views::MenuRunner> context_menu_runner_;
scoped_ptr<TouchSelectionController> touch_selection_controller_;
diff --git a/views/views.gyp b/views/views.gyp
index e82ea8b..6a6416d 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -103,6 +103,7 @@
'controls/menu/menu_config_win.cc',
'controls/menu/menu_controller.cc',
'controls/menu/menu_controller.h',
+ 'controls/menu/menu_controller_delegate.h',
'controls/menu/menu_delegate.cc',
'controls/menu/menu_delegate.h',
'controls/menu/menu_gtk.cc',