1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/menu/menu_runner_impl.h"
#include "build/build_config.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_delegate.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner_impl_adapter.h"
#include "ui/views/widget/widget.h"
#if defined(OS_WIN)
#include "ui/events/win/system_event_state_lookup.h"
#endif
namespace views {
namespace internal {
#if !defined(OS_MACOSX)
MenuRunnerImplInterface* MenuRunnerImplInterface::Create(
ui::MenuModel* menu_model,
int32_t run_types) {
return new MenuRunnerImplAdapter(menu_model);
}
#endif
MenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu)
: menu_(menu),
running_(false),
delete_after_run_(false),
async_(false),
for_drop_(false),
controller_(NULL),
owns_controller_(false),
closing_event_time_(base::TimeDelta()),
weak_factory_(this) {}
bool MenuRunnerImpl::IsRunning() const {
return running_;
}
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());
DCHECK(controller_);
// Release is invoked when MenuRunner is destroyed. Assume this is happening
// because the object referencing the menu has been destroyed and the menu
// button is no longer valid.
controller_->Cancel(MenuController::EXIT_DESTROYED);
} else {
delete this;
}
}
MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent,
MenuButton* button,
const gfx::Rect& bounds,
MenuAnchorPosition anchor,
int32_t run_types) {
closing_event_time_ = base::TimeDelta();
if (running_) {
// Ignore requests to show the menu while it's already showing. MenuItemView
// doesn't handle this very well (meaning it crashes).
return MenuRunner::NORMAL_EXIT;
}
MenuController* controller = MenuController::GetActiveInstance();
if (controller) {
if ((run_types & MenuRunner::IS_NESTED) != 0) {
if (!controller->IsBlockingRun()) {
controller->CancelAll();
controller = NULL;
}
controller->AddNestedDelegate(this);
} else {
// There's some other menu open and we're not nested. Cancel the menu.
controller->CancelAll();
if ((run_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;
async_ = (run_types & MenuRunner::ASYNC) != 0;
for_drop_ = (run_types & MenuRunner::FOR_DROP) != 0;
bool has_mnemonics = (run_types & MenuRunner::HAS_MNEMONICS) != 0;
owns_controller_ = false;
if (!controller) {
// No menus are showing, show one.
controller = new MenuController(!for_drop_, this);
owns_controller_ = true;
}
controller->SetAsyncRun(async_);
controller->set_is_combobox((run_types & MenuRunner::COMBOBOX) != 0);
controller_ = controller;
menu_->set_controller(controller_);
menu_->PrepareForRun(owns_controller_,
has_mnemonics,
!for_drop_ && ShouldShowMnemonics(button));
// Run the loop.
int mouse_event_flags = 0;
MenuItemView* result =
controller->Run(parent,
button,
menu_,
bounds,
anchor,
(run_types & MenuRunner::CONTEXT_MENU) != 0,
(run_types & MenuRunner::NESTED_DRAG) != 0,
&mouse_event_flags);
// Get the time of the event which closed this menu.
closing_event_time_ = controller->closing_event_time();
if (for_drop_ || async_) {
// Drop and asynchronous menus return immediately. We finish processing in
// OnMenuClosed.
return MenuRunner::NORMAL_EXIT;
}
return MenuDone(NOTIFY_DELEGATE, result, mouse_event_flags);
}
void MenuRunnerImpl::Cancel() {
if (running_)
controller_->Cancel(MenuController::EXIT_ALL);
}
base::TimeDelta MenuRunnerImpl::GetClosingEventTime() const {
return closing_event_time_;
}
void MenuRunnerImpl::OnMenuClosed(NotifyType type,
MenuItemView* menu,
int mouse_event_flags) {
MenuDone(type, menu, mouse_event_flags);
}
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(NotifyType type,
MenuItemView* result,
int mouse_event_flags) {
menu_->RemoveEmptyMenus();
menu_->set_controller(nullptr);
if (owns_controller_) {
// We created the controller and need to delete it.
delete controller_;
owns_controller_ = false;
}
controller_ = nullptr;
// Make sure all the windows we created to show the menus have been
// destroyed.
menu_->DestroyAllMenuHosts();
if (delete_after_run_) {
delete this;
return MenuRunner::MENU_DELETED;
}
running_ = false;
if (menu_->GetDelegate()) {
// Executing the command may also delete this.
base::WeakPtr<MenuRunnerImpl> ref(weak_factory_.GetWeakPtr());
if (result && !for_drop_) {
// Do not execute the menu that was dragged/dropped.
menu_->GetDelegate()->ExecuteCommand(result->GetCommand(),
mouse_event_flags);
}
// Only notify the delegate if it did not delete this.
if (!ref)
return MenuRunner::MENU_DELETED;
else if (type == NOTIFY_DELEGATE)
menu_->GetDelegate()->OnMenuClosed(result, MenuRunner::NORMAL_EXIT);
}
return MenuRunner::NORMAL_EXIT;
}
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)
// This is only needed on Windows.
if (!show_mnemonics)
show_mnemonics = ui::win::IsAltPressed();
#endif
return show_mnemonics;
}
} // namespace internal
} // namespace views
|