OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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_controller.h" | 5 #include "views/controls/menu/menu_controller.h" |
6 | 6 |
7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
8 #include "app/os_exchange_data.h" | 8 #include "app/os_exchange_data.h" |
9 #include "base/i18n/rtl.h" | 9 #include "base/i18n/rtl.h" |
10 #include "base/keyboard_codes.h" | 10 #include "base/keyboard_codes.h" |
11 #include "base/time.h" | 11 #include "base/time.h" |
12 #include "gfx/canvas.h" | 12 #include "gfx/canvas.h" |
13 #include "views/controls/button/menu_button.h" | 13 #include "views/controls/button/menu_button.h" |
14 #include "views/controls/menu/menu_scroll_view_container.h" | 14 #include "views/controls/menu/menu_scroll_view_container.h" |
15 #include "views/controls/menu/submenu_view.h" | 15 #include "views/controls/menu/submenu_view.h" |
16 #include "views/drag_utils.h" | 16 #include "views/drag_utils.h" |
17 #include "views/screen.h" | 17 #include "views/screen.h" |
18 #include "views/view_constants.h" | 18 #include "views/view_constants.h" |
| 19 #include "views/views_delegate.h" |
19 #include "views/widget/root_view.h" | 20 #include "views/widget/root_view.h" |
20 #include "views/widget/widget.h" | 21 #include "views/widget/widget.h" |
| 22 |
21 #if defined(OS_LINUX) | 23 #if defined(OS_LINUX) |
22 #include "base/keyboard_code_conversion_gtk.h" | 24 #include "base/keyboard_code_conversion_gtk.h" |
23 #endif | 25 #endif |
24 | 26 |
25 using base::Time; | 27 using base::Time; |
26 using base::TimeDelta; | 28 using base::TimeDelta; |
27 | 29 |
28 // Period of the scroll timer (in milliseconds). | 30 // Period of the scroll timer (in milliseconds). |
29 static const int kScrollTimerMS = 30; | 31 static const int kScrollTimerMS = 30; |
30 | 32 |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 return NULL; | 186 return NULL; |
185 } else if (button) { | 187 } else if (button) { |
186 menu_button_ = button; | 188 menu_button_ = button; |
187 } | 189 } |
188 | 190 |
189 #ifdef DEBUG_MENU | 191 #ifdef DEBUG_MENU |
190 nested_depth++; | 192 nested_depth++; |
191 DLOG(INFO) << " entering nested loop, depth=" << nested_depth; | 193 DLOG(INFO) << " entering nested loop, depth=" << nested_depth; |
192 #endif | 194 #endif |
193 | 195 |
| 196 // Make sure Chrome doesn't attempt to shut down while the menu is showing. |
| 197 if (ViewsDelegate::views_delegate) |
| 198 ViewsDelegate::views_delegate->AddRef(); |
| 199 |
194 MessageLoopForUI* loop = MessageLoopForUI::current(); | 200 MessageLoopForUI* loop = MessageLoopForUI::current(); |
195 if (MenuItemView::allow_task_nesting_during_run_) { | 201 if (MenuItemView::allow_task_nesting_during_run_) { |
196 bool did_allow_task_nesting = loop->NestableTasksAllowed(); | 202 bool did_allow_task_nesting = loop->NestableTasksAllowed(); |
197 loop->SetNestableTasksAllowed(true); | 203 loop->SetNestableTasksAllowed(true); |
198 loop->Run(this); | 204 loop->Run(this); |
199 loop->SetNestableTasksAllowed(did_allow_task_nesting); | 205 loop->SetNestableTasksAllowed(did_allow_task_nesting); |
200 } else { | 206 } else { |
201 loop->Run(this); | 207 loop->Run(this); |
202 } | 208 } |
203 | 209 |
| 210 if (ViewsDelegate::views_delegate) |
| 211 ViewsDelegate::views_delegate->ReleaseRef(); |
| 212 |
204 #ifdef DEBUG_MENU | 213 #ifdef DEBUG_MENU |
205 nested_depth--; | 214 nested_depth--; |
206 DLOG(INFO) << " exiting nested loop, depth=" << nested_depth; | 215 DLOG(INFO) << " exiting nested loop, depth=" << nested_depth; |
207 #endif | 216 #endif |
208 | 217 |
209 // Close any open menus. | 218 // Close any open menus. |
210 SetSelection(NULL, false, true); | 219 SetSelection(NULL, false, true); |
211 | 220 |
212 if (nested_menu) { | 221 if (nested_menu) { |
213 DCHECK(!menu_stack_.empty()); | 222 DCHECK(!menu_stack_.empty()); |
(...skipping 17 matching lines...) Expand all Loading... |
231 if (exit_type_ == EXIT_OUTERMOST) { | 240 if (exit_type_ == EXIT_OUTERMOST) { |
232 exit_type_ = EXIT_NONE; | 241 exit_type_ = EXIT_NONE; |
233 } else { | 242 } else { |
234 if (nested_menu && result) { | 243 if (nested_menu && result) { |
235 // We're nested and about to return a value. The caller might enter | 244 // We're nested and about to return a value. The caller might enter |
236 // another blocking loop. We need to make sure all menus are hidden | 245 // another blocking loop. We need to make sure all menus are hidden |
237 // before that happens otherwise the menus will stay on screen. | 246 // before that happens otherwise the menus will stay on screen. |
238 CloseAllNestedMenus(); | 247 CloseAllNestedMenus(); |
239 | 248 |
240 // Set exit_all_, which makes sure all nested loops exit immediately. | 249 // Set exit_all_, which makes sure all nested loops exit immediately. |
241 exit_type_ = EXIT_ALL; | 250 if (exit_type_ != EXIT_DESTROYED) |
| 251 exit_type_ = EXIT_ALL; |
242 } | 252 } |
243 } | 253 } |
244 | 254 |
245 if (menu_button_) { | 255 // If we stopped running because one of the menus was destroyed chances are |
| 256 // the button was also destroyed. |
| 257 if (exit_type_ != EXIT_DESTROYED && menu_button_) { |
246 menu_button_->SetState(CustomButton::BS_NORMAL); | 258 menu_button_->SetState(CustomButton::BS_NORMAL); |
247 menu_button_->SchedulePaint(); | 259 menu_button_->SchedulePaint(); |
248 } | 260 } |
249 | 261 |
250 return result; | 262 return result; |
251 } | 263 } |
252 | 264 |
253 void MenuController::SetSelection(MenuItemView* menu_item, | 265 void MenuController::SetSelection(MenuItemView* menu_item, |
254 bool open_submenu, | 266 bool open_submenu, |
255 bool update_immediately) { | 267 bool update_immediately) { |
(...skipping 23 matching lines...) Expand all Loading... |
279 // Stop timers. | 291 // Stop timers. |
280 StopShowTimer(); | 292 StopShowTimer(); |
281 StopCancelAllTimer(); | 293 StopCancelAllTimer(); |
282 | 294 |
283 if (update_immediately) | 295 if (update_immediately) |
284 CommitPendingSelection(); | 296 CommitPendingSelection(); |
285 else | 297 else |
286 StartShowTimer(); | 298 StartShowTimer(); |
287 } | 299 } |
288 | 300 |
289 void MenuController::Cancel(bool all) { | 301 void MenuController::Cancel(ExitType type) { |
290 if (!showing_) { | 302 if (!showing_) { |
291 // This occurs if we're in the process of notifying the delegate for a drop | 303 // This occurs if we're in the process of notifying the delegate for a drop |
292 // and the delegate cancels us. | 304 // and the delegate cancels us. |
293 return; | 305 return; |
294 } | 306 } |
295 | 307 |
296 MenuItemView* selected = state_.item; | 308 MenuItemView* selected = state_.item; |
297 exit_type_ = all ? EXIT_ALL : EXIT_OUTERMOST; | 309 exit_type_ = type; |
298 | 310 |
299 // Hide windows immediately. | 311 // Hide windows immediately. |
300 SetSelection(NULL, false, true); | 312 SetSelection(NULL, false, true); |
301 | 313 |
302 if (!blocking_run_) { | 314 if (!blocking_run_) { |
303 // If we didn't block the caller we need to notify the menu, which | 315 // If we didn't block the caller we need to notify the menu, which |
304 // triggers deleting us. | 316 // triggers deleting us. |
305 DCHECK(selected); | 317 DCHECK(selected); |
306 showing_ = false; | 318 showing_ = false; |
307 selected->GetRootMenuItem()->DropMenuClosed(true); | 319 selected->GetRootMenuItem()->DropMenuClosed(true); |
(...skipping 21 matching lines...) Expand all Loading... |
329 | 341 |
330 // We're going to close and we own the mouse capture. We need to repost the | 342 // We're going to close and we own the mouse capture. We need to repost the |
331 // mouse down, otherwise the window the user clicked on won't get the | 343 // mouse down, otherwise the window the user clicked on won't get the |
332 // event. | 344 // event. |
333 #if defined(OS_WIN) | 345 #if defined(OS_WIN) |
334 RepostEvent(source, event); | 346 RepostEvent(source, event); |
335 // NOTE: not reposting on linux seems fine. | 347 // NOTE: not reposting on linux seems fine. |
336 #endif | 348 #endif |
337 | 349 |
338 // And close. | 350 // And close. |
339 Cancel(true); | 351 Cancel(EXIT_ALL); |
340 return; | 352 return; |
341 } | 353 } |
342 | 354 |
343 bool open_submenu = false; | 355 bool open_submenu = false; |
344 if (!part.menu) { | 356 if (!part.menu) { |
345 part.menu = part.parent; | 357 part.menu = part.parent; |
346 open_submenu = true; | 358 open_submenu = true; |
347 } else { | 359 } else { |
348 if (part.menu->GetDelegate()->CanDrag(part.menu)) { | 360 if (part.menu->GetDelegate()->CanDrag(part.menu)) { |
349 possible_drag_ = true; | 361 possible_drag_ = true; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 drag_utils::SetDragImageOnDataObject(canvas, item->size(), press_loc, | 399 drag_utils::SetDragImageOnDataObject(canvas, item->size(), press_loc, |
388 &data); | 400 &data); |
389 StopScrolling(); | 401 StopScrolling(); |
390 int drag_ops = item->GetDelegate()->GetDragOperations(item); | 402 int drag_ops = item->GetDelegate()->GetDragOperations(item); |
391 item->GetRootView()->StartDragForViewFromMouseEvent( | 403 item->GetRootView()->StartDragForViewFromMouseEvent( |
392 NULL, data, drag_ops); | 404 NULL, data, drag_ops); |
393 if (GetActiveInstance() == this) { | 405 if (GetActiveInstance() == this) { |
394 if (showing_) { | 406 if (showing_) { |
395 // We're still showing, close all menus. | 407 // We're still showing, close all menus. |
396 CloseAllNestedMenus(); | 408 CloseAllNestedMenus(); |
397 Cancel(true); | 409 Cancel(EXIT_ALL); |
398 } // else case, drop was on us. | 410 } // else case, drop was on us. |
399 } // else case, someone canceled us, don't do anything | 411 } // else case, someone canceled us, don't do anything |
400 } | 412 } |
401 return; | 413 return; |
402 } | 414 } |
403 if (part.type == MenuPart::MENU_ITEM) { | 415 if (part.type == MenuPart::MENU_ITEM) { |
404 if (!part.menu) | 416 if (!part.menu) |
405 part.menu = source->GetMenuItem(); | 417 part.menu = source->GetMenuItem(); |
406 SetSelection(part.menu ? part.menu : state_.item, true, false); | 418 SetSelection(part.menu ? part.menu : state_.item, true, false); |
407 } else if (part.type == MenuPart::NONE) { | 419 } else if (part.type == MenuPart::NONE) { |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 | 642 |
631 // static | 643 // static |
632 void MenuController::SetActiveInstance(MenuController* controller) { | 644 void MenuController::SetActiveInstance(MenuController* controller) { |
633 active_instance_ = controller; | 645 active_instance_ = controller; |
634 } | 646 } |
635 | 647 |
636 #if defined(OS_WIN) | 648 #if defined(OS_WIN) |
637 bool MenuController::Dispatch(const MSG& msg) { | 649 bool MenuController::Dispatch(const MSG& msg) { |
638 DCHECK(blocking_run_); | 650 DCHECK(blocking_run_); |
639 | 651 |
640 if (exit_type_ == EXIT_ALL) { | 652 if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) { |
641 // We must translate/dispatch the message here, otherwise we would drop | 653 // We must translate/dispatch the message here, otherwise we would drop |
642 // the message on the floor. | 654 // the message on the floor. |
643 TranslateMessage(&msg); | 655 TranslateMessage(&msg); |
644 DispatchMessage(&msg); | 656 DispatchMessage(&msg); |
645 return false; | 657 return false; |
646 } | 658 } |
647 | 659 |
648 // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. | 660 // NOTE: we don't get WM_ACTIVATE or anything else interesting in here. |
649 switch (msg.message) { | 661 switch (msg.message) { |
650 case WM_CONTEXTMENU: { | 662 case WM_CONTEXTMENU: { |
(...skipping 20 matching lines...) Expand all Loading... |
671 | 683 |
672 case WM_SYSKEYUP: | 684 case WM_SYSKEYUP: |
673 // We may have been shown on a system key, as such don't do anything | 685 // We may have been shown on a system key, as such don't do anything |
674 // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and | 686 // here. If another system key is pushed we'll get a WM_SYSKEYDOWN and |
675 // close the menu. | 687 // close the menu. |
676 return true; | 688 return true; |
677 | 689 |
678 case WM_CANCELMODE: | 690 case WM_CANCELMODE: |
679 case WM_SYSKEYDOWN: | 691 case WM_SYSKEYDOWN: |
680 // Exit immediately on system keys. | 692 // Exit immediately on system keys. |
681 Cancel(true); | 693 Cancel(EXIT_ALL); |
682 return false; | 694 return false; |
683 | 695 |
684 default: | 696 default: |
685 break; | 697 break; |
686 } | 698 } |
687 TranslateMessage(&msg); | 699 TranslateMessage(&msg); |
688 DispatchMessage(&msg); | 700 DispatchMessage(&msg); |
689 return exit_type_ == EXIT_NONE; | 701 return exit_type_ == EXIT_NONE; |
690 } | 702 } |
691 | 703 |
692 #else | 704 #else |
693 bool MenuController::Dispatch(GdkEvent* event) { | 705 bool MenuController::Dispatch(GdkEvent* event) { |
694 gtk_main_do_event(event); | 706 gtk_main_do_event(event); |
695 | 707 |
696 if (exit_type_ == EXIT_ALL) | 708 if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) |
697 return false; | 709 return false; |
698 | 710 |
699 switch (event->type) { | 711 switch (event->type) { |
700 case GDK_KEY_PRESS: { | 712 case GDK_KEY_PRESS: { |
701 base::KeyboardCode win_keycode = | 713 base::KeyboardCode win_keycode = |
702 base::WindowsKeyCodeForGdkKeyCode(event->key.keyval); | 714 base::WindowsKeyCodeForGdkKeyCode(event->key.keyval); |
703 if (!OnKeyDown(win_keycode)) | 715 if (!OnKeyDown(win_keycode)) |
704 return false; | 716 return false; |
705 guint32 keycode = gdk_keyval_to_unicode(event->key.keyval); | 717 guint32 keycode = gdk_keyval_to_unicode(event->key.keyval); |
706 if (keycode) | 718 if (keycode) |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
759 } | 771 } |
760 } | 772 } |
761 break; | 773 break; |
762 | 774 |
763 case base::VKEY_ESCAPE: | 775 case base::VKEY_ESCAPE: |
764 if (!state_.item->GetParentMenuItem() || | 776 if (!state_.item->GetParentMenuItem() || |
765 (!state_.item->GetParentMenuItem()->GetParentMenuItem() && | 777 (!state_.item->GetParentMenuItem()->GetParentMenuItem() && |
766 (!state_.item->HasSubmenu() || | 778 (!state_.item->HasSubmenu() || |
767 !state_.item->GetSubmenu()->IsShowing()))) { | 779 !state_.item->GetSubmenu()->IsShowing()))) { |
768 // User pressed escape and only one menu is shown, cancel it. | 780 // User pressed escape and only one menu is shown, cancel it. |
769 Cancel(false); | 781 Cancel(EXIT_OUTERMOST); |
770 return false; | 782 return false; |
771 } else { | 783 } else { |
772 CloseSubmenu(); | 784 CloseSubmenu(); |
773 } | 785 } |
774 break; | 786 break; |
775 | 787 |
776 #if defined(OS_WIN) | 788 #if defined(OS_WIN) |
777 case VK_APPS: | 789 case VK_APPS: |
778 break; | 790 break; |
779 #endif | 791 #endif |
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1501 if (!scroll_task_.get()) | 1513 if (!scroll_task_.get()) |
1502 scroll_task_.reset(new MenuScrollTask()); | 1514 scroll_task_.reset(new MenuScrollTask()); |
1503 scroll_task_->Update(part); | 1515 scroll_task_->Update(part); |
1504 } | 1516 } |
1505 | 1517 |
1506 void MenuController::StopScrolling() { | 1518 void MenuController::StopScrolling() { |
1507 scroll_task_.reset(NULL); | 1519 scroll_task_.reset(NULL); |
1508 } | 1520 } |
1509 | 1521 |
1510 } // namespace views | 1522 } // namespace views |
OLD | NEW |