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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
// 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.
#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 {
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 MenuRunnerImpl(MenuItemView* menu);
MenuItemView* menu() { return menu_; }
// See description above class for details.
void Release();
// Runs the menu.
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:
~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_;
// 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_;
// Set if |running_| and Release() has been invoked.
bool delete_after_run_;
// 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);
};
MenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu)
: menu_(menu),
running_(false),
delete_after_run_(false),
for_drop_(false),
controller_(NULL),
owns_controller_(false) {
}
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,
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 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;
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 MenuRunner::MENU_DELETED;
}
running_ = false;
if (result && menu_->GetDelegate()) {
menu_->GetDelegate()->ExecuteCommand(result->GetCommand(),
mouse_event_flags);
}
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)
// 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;
}
} // namespace internal
MenuRunner::MenuRunner(MenuItemView* menu)
: holder_(new internal::MenuRunnerImpl(menu)) {
}
MenuRunner::~MenuRunner() {
holder_->Release();
}
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
|