Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(390)

Side by Side Diff: views/controls/menu/menu_runner.cc

Issue 7720012: Moves ownership of MenuItemView to MenuRunner as well as responbility (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix unit test Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "views/controls/menu/menu_runner.h" 5 #include "views/controls/menu/menu_runner.h"
6 6
7 #include <set>
8
9 #include "views/controls/button/menu_button.h"
10 #include "views/controls/menu/menu_controller.h"
11 #include "views/controls/menu/menu_controller_delegate.h"
12 #include "views/controls/menu/menu_delegate.h"
13
14 #if defined(OS_WIN)
15 #include "base/win/win_util.h"
16 #endif
17
7 namespace views { 18 namespace views {
8 19
9 // Manages the menu. To destroy a Holder invoke Release(). Release() deletes 20 namespace internal {
10 // immediately if the menu isn't showing. If the menu is showing Release() 21
11 // cancels the menu and when the nested RunMenuAt() call returns, deletes itself 22 // Manages the menu. To destroy a MenuRunnerImpl invoke Release(). Release()
12 // and the menu. 23 // deletes immediately if the menu isn't showing. If the menu is showing
13 class MenuRunner::Holder { 24 // Release() cancels the menu and when the nested RunMenuAt() call returns
25 // deletes itself and the menu.
26 class MenuRunnerImpl : public internal::MenuControllerDelegate {
14 public: 27 public:
15 explicit Holder(MenuItemView* menu); 28 explicit MenuRunnerImpl(MenuItemView* menu);
29
30 MenuItemView* menu() { return menu_; }
16 31
17 // See description above class for details. 32 // See description above class for details.
18 void Release(); 33 void Release();
19 34
20 // Runs the menu. 35 // Runs the menu.
21 void RunMenuAt(Widget* parent, 36 MenuRunner::RunResult RunMenuAt(Widget* parent,
22 MenuButton* button, 37 MenuButton* button,
23 const gfx::Rect& bounds, 38 const gfx::Rect& bounds,
24 MenuItemView::AnchorPosition anchor, 39 MenuItemView::AnchorPosition anchor,
25 bool has_mnemonics); 40 int32 types) WARN_UNUSED_RESULT;
41
42 void Cancel();
43
44 // MenuControllerDelegate:
45 virtual void DropMenuClosed(NotifyType type, MenuItemView* menu) OVERRIDE;
46 virtual void SiblingMenuCreated(MenuItemView* menu) OVERRIDE;
26 47
27 private: 48 private:
28 ~Holder(); 49 ~MenuRunnerImpl();
29 50
30 scoped_ptr<MenuItemView> menu_; 51 // Cleans up after the menu is no longer showing. |result| is the menu that
52 // the user selected, or NULL if nothing was selected.
53 MenuRunner::RunResult MenuDone(MenuItemView* result, int mouse_event_flags);
54
55 // Returns true if mnemonics should be shown in the menu.
56 bool ShouldShowMnemonics(MenuButton* button);
57
58 // The menu. We own this. We don't use scoped_ptr as the destructor is
59 // protected and we're a friend.
60 MenuItemView* menu_;
61
62 // Any sibling menus. Does not include |menu_|. We own these too.
63 std::set<MenuItemView*> sibling_menus_;
64
65 // Created and set as the delegate of the MenuItemView if Release() is
66 // invoked. This is done to make sure the delegate isn't notified after
67 // Release() is invoked. We do this as we assume the delegate is no longer
68 // valid if MenuRunner has been deleted.
69 scoped_ptr<MenuDelegate> empty_delegate_;
31 70
32 // Are we in run waiting for it to return? 71 // Are we in run waiting for it to return?
33 bool running_; 72 bool running_;
34 73
35 // Set if |running_| and Release() has been invoked. 74 // Set if |running_| and Release() has been invoked.
36 bool delete_after_run_; 75 bool delete_after_run_;
37 76
38 DISALLOW_COPY_AND_ASSIGN(Holder); 77 // Are we running for a drop?
78 bool for_drop_;
79
80 // The controller.
81 MenuController* controller_;
82
83 // Do we own the controller?
84 bool owns_controller_;
85
86 DISALLOW_COPY_AND_ASSIGN(MenuRunnerImpl);
39 }; 87 };
40 88
41 MenuRunner::Holder::Holder(MenuItemView* menu) 89 MenuRunnerImpl::MenuRunnerImpl(MenuItemView* menu)
42 : menu_(menu), 90 : menu_(menu),
43 running_(false), 91 running_(false),
44 delete_after_run_(false) { 92 delete_after_run_(false),
93 for_drop_(false),
94 controller_(NULL),
95 owns_controller_(false) {
45 } 96 }
46 97
47 void MenuRunner::Holder::Release() { 98 void MenuRunnerImpl::Release() {
48 if (running_) { 99 if (running_) {
100 if (delete_after_run_)
101 return; // We already canceled.
102
49 // The menu is running a nested message loop, we can't delete it now 103 // The menu is running a nested message loop, we can't delete it now
50 // otherwise the stack would be in a really bad state (many frames would 104 // otherwise the stack would be in a really bad state (many frames would
51 // have deleted objects on them). Instead cancel the menu, when it returns 105 // have deleted objects on them). Instead cancel the menu, when it returns
52 // Holder will delete itself. 106 // Holder will delete itself.
53 delete_after_run_ = true; 107 delete_after_run_ = true;
108
109 // Swap in a different delegate. That way we know the original MenuDelegate
110 // won't be notified later on (when it's likely already been deleted).
111 if (!empty_delegate_.get())
112 empty_delegate_.reset(new MenuDelegate());
113 menu_->set_delegate(empty_delegate_.get());
114
54 menu_->Cancel(); 115 menu_->Cancel();
55 } else { 116 } else {
56 delete this; 117 delete this;
57 } 118 }
58 } 119 }
59 120
60 void MenuRunner::Holder::RunMenuAt(Widget* parent, 121 MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(
61 MenuButton* button, 122 Widget* parent,
62 const gfx::Rect& bounds, 123 MenuButton* button,
63 MenuItemView::AnchorPosition anchor, 124 const gfx::Rect& bounds,
64 bool has_mnemonics) { 125 MenuItemView::AnchorPosition anchor,
126 int32 types) {
65 if (running_) { 127 if (running_) {
66 // Ignore requests to show the menu while it's already showing. MenuItemView 128 // Ignore requests to show the menu while it's already showing. MenuItemView
67 // doesn't handle this very well (meaning it crashes). 129 // doesn't handle this very well (meaning it crashes).
68 return; 130 return MenuRunner::NORMAL_EXIT;
131 }
132
133 MenuController* controller = MenuController::GetActiveInstance();
134 if (controller) {
135 if ((types & MenuRunner::IS_NESTED) != 0) {
136 if (!controller->IsBlockingRun()) {
137 controller->CancelAll();
138 controller = NULL;
139 }
140 } else {
141 // There's some other menu open and we're not nested. Cancel the menu.
142 controller->CancelAll();
143 if ((types & MenuRunner::FOR_DROP) == 0) {
144 // We can't open another menu, otherwise the message loop would become
145 // twice nested. This isn't necessarily a problem, but generally isn't
146 // expected.
147 return MenuRunner::NORMAL_EXIT;
148 }
149 // Drop menus don't block the message loop, so it's ok to create a new
150 // MenuController.
151 controller = NULL;
152 }
69 } 153 }
70 running_ = true; 154 running_ = true;
71 menu_->RunMenuAt(parent, button, bounds, anchor, has_mnemonics); 155 for_drop_ = (types & MenuRunner::FOR_DROP) != 0;
156 bool has_mnemonics = (types & MenuRunner::HAS_MNEMONICS) != 0 && !for_drop_;
157 menu_->PrepareForRun(has_mnemonics,
158 !for_drop_ && ShouldShowMnemonics(button));
159 int mouse_event_flags = 0;
160 owns_controller_ = false;
161 if (!controller) {
162 // No menus are showing, show one.
163 controller = new MenuController(!for_drop_, this);
164 owns_controller_ = true;
165 }
166 controller_ = controller;
167 menu_->set_controller(controller_);
168
169 // Run the loop.
170 MenuItemView* result = controller->Run(parent, button, menu_, bounds, anchor,
171 &mouse_event_flags);
172
173 if (for_drop_) {
174 // Drop menus return immediately. We finish processing in DropMenuClosed.
175 return MenuRunner::NORMAL_EXIT;
176 }
177
178 return MenuDone(result, mouse_event_flags);
179 }
180
181 void MenuRunnerImpl::Cancel() {
182 if (running_)
183 controller_->Cancel(MenuController::EXIT_ALL);
184 }
185
186 void MenuRunnerImpl::DropMenuClosed(NotifyType type, MenuItemView* menu) {
187 MenuDone(NULL, 0);
188
189 if (type == NOTIFY_DELEGATE && menu->GetDelegate()) {
190 // Delegate is null when invoked from the destructor.
191 menu->GetDelegate()->DropMenuClosed(menu);
192 }
193 }
194
195 void MenuRunnerImpl::SiblingMenuCreated(MenuItemView* menu) {
196 if (menu != menu_ && sibling_menus_.count(menu) == 0)
197 sibling_menus_.insert(menu);
198 }
199
200 MenuRunnerImpl::~MenuRunnerImpl() {
201 delete menu_;
202 for (std::set<MenuItemView*>::iterator i = sibling_menus_.begin();
203 i != sibling_menus_.end(); ++i)
204 delete *i;
205 }
206
207 MenuRunner::RunResult MenuRunnerImpl::MenuDone(MenuItemView* result,
208 int mouse_event_flags) {
209 menu_->RemoveEmptyMenus();
210 menu_->set_controller(NULL);
211
212 if (owns_controller_) {
213 // We created the controller and need to delete it.
214 delete controller_;
215 owns_controller_ = false;
216 }
217 controller_ = NULL;
218 // Make sure all the windows we created to show the menus have been
219 // destroyed.
220 menu_->DestroyAllMenuHosts();
72 if (delete_after_run_) { 221 if (delete_after_run_) {
73 delete this; 222 delete this;
74 return; 223 return MenuRunner::MENU_DELETED;
75 } 224 }
76 running_ = false; 225 running_ = false;
226 if (result && menu_->GetDelegate()) {
227 menu_->GetDelegate()->ExecuteCommand(result->GetCommand(),
228 mouse_event_flags);
229 }
230 return MenuRunner::NORMAL_EXIT;
77 } 231 }
78 232
79 MenuRunner::Holder::~Holder() { 233 bool MenuRunnerImpl::ShouldShowMnemonics(MenuButton* button) {
234 // Show mnemonics if the button has focus or alt is pressed.
235 bool show_mnemonics = button ? button->HasFocus() : false;
236 #if defined(OS_WIN)
237 // We don't currently need this on gtk as showing the menu gives focus to the
238 // button first.
239 if (!show_mnemonics)
240 show_mnemonics = base::win::IsAltPressed();
241 #endif
242 return show_mnemonics;
80 } 243 }
81 244
82 MenuRunner::MenuRunner(MenuItemView* menu) : holder_(new Holder(menu)) { 245 } // namespace internal
246
247 MenuRunner::MenuRunner(MenuItemView* menu)
248 : holder_(new internal::MenuRunnerImpl(menu)) {
83 } 249 }
84 250
85 MenuRunner::~MenuRunner() { 251 MenuRunner::~MenuRunner() {
86 holder_->Release(); 252 holder_->Release();
87 } 253 }
88 254
89 void MenuRunner::RunMenuAt(Widget* parent, 255 MenuItemView* MenuRunner::GetMenu() {
90 MenuButton* button, 256 return holder_->menu();
91 const gfx::Rect& bounds, 257 }
92 MenuItemView::AnchorPosition anchor, 258
93 bool has_mnemonics) { 259 MenuRunner::RunResult MenuRunner::RunMenuAt(Widget* parent,
94 holder_->RunMenuAt(parent, button, bounds, anchor, has_mnemonics); 260 MenuButton* button,
261 const gfx::Rect& bounds,
262 MenuItemView::AnchorPosition anchor,
263 int32 types) {
264 return holder_->RunMenuAt(parent, button, bounds, anchor, types);
265 }
266
267 void MenuRunner::Cancel() {
268 holder_->Cancel();
95 } 269 }
96 270
97 } // namespace views 271 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698