1116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
2116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// found in the LICENSE file.
4116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
5116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/menu/menu_runner_impl.h"
6116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
7116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/native_theme/native_theme.h"
8116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/button/menu_button.h"
9116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/menu/menu_controller.h"
10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/menu/menu_delegate.h"
11116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/menu/menu_item_view.h"
12116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/controls/menu/menu_runner_impl_adapter.h"
13116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/views/widget/widget.h"
14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
15116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_WIN)
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/win/win_util.h"
17116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
19116680a4aac90f2aa7413d9095a592090648e557Ben Murdochnamespace views {
20116680a4aac90f2aa7413d9095a592090648e557Ben Murdochnamespace internal {
21116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if !defined(OS_MACOSX)
23116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMenuRunnerImplInterface* MenuRunnerImplInterface::Create(
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    ui::MenuModel* menu_model,
25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    int32 run_types) {
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return new MenuRunnerImplAdapter(menu_model);
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
28116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
30116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu)
31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : menu_(menu),
32116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      running_(false),
33116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      delete_after_run_(false),
34116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      for_drop_(false),
35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      controller_(NULL),
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      owns_controller_(false),
37116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      closing_event_time_(base::TimeDelta()),
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      weak_factory_(this) {
39116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
41116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool MenuRunnerImpl::IsRunning() const {
42116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return running_;
43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
45116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MenuRunnerImpl::Release() {
46116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (running_) {
47116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (delete_after_run_)
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return;  // We already canceled.
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // The menu is running a nested message loop, we can't delete it now
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // otherwise the stack would be in a really bad state (many frames would
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // have deleted objects on them). Instead cancel the menu, when it returns
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Holder will delete itself.
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    delete_after_run_ = true;
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Swap in a different delegate. That way we know the original MenuDelegate
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // won't be notified later on (when it's likely already been deleted).
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!empty_delegate_.get())
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      empty_delegate_.reset(new MenuDelegate());
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    menu_->set_delegate(empty_delegate_.get());
61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    DCHECK(controller_);
63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Release is invoked when MenuRunner is destroyed. Assume this is happening
64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // because the object referencing the menu has been destroyed and the menu
65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // button is no longer valid.
66116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    controller_->Cancel(MenuController::EXIT_DESTROYED);
67116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  } else {
68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    delete this;
69116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
70116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
72116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent,
73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                MenuButton* button,
74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                const gfx::Rect& bounds,
75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                MenuAnchorPosition anchor,
76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                                int32 run_types) {
77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  closing_event_time_ = base::TimeDelta();
78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (running_) {
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Ignore requests to show the menu while it's already showing. MenuItemView
80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // doesn't handle this very well (meaning it crashes).
81116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return MenuRunner::NORMAL_EXIT;
82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  MenuController* controller = MenuController::GetActiveInstance();
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (controller) {
86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if ((run_types & MenuRunner::IS_NESTED) != 0) {
87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if (!controller->IsBlockingRun()) {
88116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        controller->CancelAll();
89116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        controller = NULL;
90116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      }
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    } else {
92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // There's some other menu open and we're not nested. Cancel the menu.
93116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      controller->CancelAll();
94116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if ((run_types & MenuRunner::FOR_DROP) == 0) {
95116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        // We can't open another menu, otherwise the message loop would become
96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        // twice nested. This isn't necessarily a problem, but generally isn't
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        // expected.
98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        return MenuRunner::NORMAL_EXIT;
99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      }
100116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // Drop menus don't block the message loop, so it's ok to create a new
101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      // MenuController.
102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      controller = NULL;
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  running_ = true;
107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for_drop_ = (run_types & MenuRunner::FOR_DROP) != 0;
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  bool has_mnemonics = (run_types & MenuRunner::HAS_MNEMONICS) != 0;
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  owns_controller_ = false;
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!controller) {
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // No menus are showing, show one.
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    ui::NativeTheme* theme =
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        parent ? parent->GetNativeTheme() : ui::NativeTheme::instance();
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    controller = new MenuController(theme, !for_drop_, this);
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    owns_controller_ = true;
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  controller->set_is_combobox((run_types & MenuRunner::COMBOBOX) != 0);
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  controller_ = controller;
119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_->set_controller(controller_);
120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_->PrepareForRun(owns_controller_,
121116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       has_mnemonics,
122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       !for_drop_ && ShouldShowMnemonics(button));
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
124116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Run the loop.
125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  int mouse_event_flags = 0;
126116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  MenuItemView* result =
127116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      controller->Run(parent,
128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      button,
129116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      menu_,
130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      bounds,
131116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      anchor,
132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      (run_types & MenuRunner::CONTEXT_MENU) != 0,
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      (run_types & MenuRunner::NESTED_DRAG) != 0,
134116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      &mouse_event_flags);
135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Get the time of the event which closed this menu.
136116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  closing_event_time_ = controller->closing_event_time();
137116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (for_drop_) {
138116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Drop menus return immediately. We finish processing in DropMenuClosed.
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return MenuRunner::NORMAL_EXIT;
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return MenuDone(result, mouse_event_flags);
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
144116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MenuRunnerImpl::Cancel() {
145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (running_)
146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    controller_->Cancel(MenuController::EXIT_ALL);
147116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
148116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbase::TimeDelta MenuRunnerImpl::GetClosingEventTime() const {
150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return closing_event_time_;
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
153116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MenuRunnerImpl::DropMenuClosed(NotifyType type, MenuItemView* menu) {
154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  MenuDone(NULL, 0);
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (type == NOTIFY_DELEGATE && menu->GetDelegate()) {
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Delegate is null when invoked from the destructor.
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    menu->GetDelegate()->DropMenuClosed(menu);
159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid MenuRunnerImpl::SiblingMenuCreated(MenuItemView* menu) {
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (menu != menu_ && sibling_menus_.count(menu) == 0)
164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    sibling_menus_.insert(menu);
165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
166116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
167116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMenuRunnerImpl::~MenuRunnerImpl() {
168116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  delete menu_;
169116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for (std::set<MenuItemView*>::iterator i = sibling_menus_.begin();
170116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch       i != sibling_menus_.end();
171116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch       ++i)
172116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    delete *i;
173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
175116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMenuRunner::RunResult MenuRunnerImpl::MenuDone(MenuItemView* result,
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                               int mouse_event_flags) {
177116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_->RemoveEmptyMenus();
178116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_->set_controller(NULL);
179116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
180116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (owns_controller_) {
181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // We created the controller and need to delete it.
182116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    delete controller_;
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    owns_controller_ = false;
184116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
185116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  controller_ = NULL;
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Make sure all the windows we created to show the menus have been
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // destroyed.
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_->DestroyAllMenuHosts();
189116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (delete_after_run_) {
190116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    delete this;
191116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return MenuRunner::MENU_DELETED;
192116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
193116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  running_ = false;
194116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (result && menu_->GetDelegate()) {
195116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Executing the command may also delete this.
196116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    base::WeakPtr<MenuRunnerImpl> ref(weak_factory_.GetWeakPtr());
197116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    menu_->GetDelegate()->ExecuteCommand(result->GetCommand(),
198116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                         mouse_event_flags);
199116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!ref)
200116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return MenuRunner::MENU_DELETED;
201116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
202116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return MenuRunner::NORMAL_EXIT;
203116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
204116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
205116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool MenuRunnerImpl::ShouldShowMnemonics(MenuButton* button) {
206116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Show mnemonics if the button has focus or alt is pressed.
207116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool show_mnemonics = button ? button->HasFocus() : false;
208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_WIN)
209116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // This is only needed on Windows.
210116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!show_mnemonics)
211116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    show_mnemonics = base::win::IsAltPressed();
212116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
213116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return show_mnemonics;
214116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
215116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
216116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}  // namespace internal
217116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}  // namespace views
218