OLD | NEW |
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_controller.h" | 5 #include "views/controls/menu/menu_controller.h" |
6 | 6 |
7 #include "base/i18n/case_conversion.h" | 7 #include "base/i18n/case_conversion.h" |
8 #include "base/i18n/rtl.h" | 8 #include "base/i18n/rtl.h" |
9 #include "base/time.h" | 9 #include "base/time.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "ui/base/dragdrop/os_exchange_data.h" | 11 #include "ui/base/dragdrop/os_exchange_data.h" |
12 #include "ui/base/events.h" | 12 #include "ui/base/events.h" |
13 #include "ui/base/keycodes/keyboard_codes.h" | 13 #include "ui/base/keycodes/keyboard_codes.h" |
14 #include "ui/base/l10n/l10n_util.h" | 14 #include "ui/base/l10n/l10n_util.h" |
15 #include "ui/gfx/canvas_skia.h" | 15 #include "ui/gfx/canvas_skia.h" |
16 #include "ui/gfx/screen.h" | 16 #include "ui/gfx/screen.h" |
17 #include "views/controls/button/menu_button.h" | 17 #include "views/controls/button/menu_button.h" |
18 #include "views/controls/menu/menu_controller_delegate.h" | 18 #include "views/controls/menu/menu_controller_delegate.h" |
19 #include "views/controls/menu/menu_scroll_view_container.h" | 19 #include "views/controls/menu/menu_scroll_view_container.h" |
20 #include "views/controls/menu/submenu_view.h" | 20 #include "views/controls/menu/submenu_view.h" |
21 #include "views/drag_utils.h" | 21 #include "views/drag_utils.h" |
22 #include "views/view_constants.h" | 22 #include "views/view_constants.h" |
23 #include "views/views_delegate.h" | 23 #include "views/views_delegate.h" |
24 #include "views/widget/root_view.h" | 24 #include "views/widget/root_view.h" |
25 #include "views/widget/widget.h" | 25 #include "views/widget/widget.h" |
26 | 26 |
27 #if defined(USE_AURA) && !defined(OS_WIN) | 27 #if defined(USE_AURA) |
28 #include "ui/aura/desktop.h" | 28 #include "ui/aura/desktop.h" |
29 #endif | 29 #endif |
30 | 30 |
31 #if defined(OS_LINUX) | 31 #if defined(TOOLKIT_USES_GTK) |
32 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" | 32 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h" |
33 #endif | 33 #endif |
34 | 34 |
35 #if defined(TOUCH_UI) | 35 #if defined(TOUCH_UI) |
36 #include "views/focus/accelerator_handler.h" | 36 #include "views/focus/accelerator_handler.h" |
37 #endif | 37 #endif |
38 | 38 |
39 using base::Time; | 39 using base::Time; |
40 using base::TimeDelta; | 40 using base::TimeDelta; |
41 using ui::OSExchangeData; | 41 using ui::OSExchangeData; |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 } | 341 } |
342 | 342 |
343 MenuItemView* result = result_; | 343 MenuItemView* result = result_; |
344 // In case we're nested, reset result_. | 344 // In case we're nested, reset result_. |
345 result_ = NULL; | 345 result_ = NULL; |
346 | 346 |
347 if (result_mouse_event_flags) | 347 if (result_mouse_event_flags) |
348 *result_mouse_event_flags = result_mouse_event_flags_; | 348 *result_mouse_event_flags = result_mouse_event_flags_; |
349 | 349 |
350 if (exit_type_ == EXIT_OUTERMOST) { | 350 if (exit_type_ == EXIT_OUTERMOST) { |
351 exit_type_ = EXIT_NONE; | 351 SetExitType(EXIT_NONE); |
352 } else { | 352 } else { |
353 if (nested_menu && result) { | 353 if (nested_menu && result) { |
354 // We're nested and about to return a value. The caller might enter | 354 // We're nested and about to return a value. The caller might enter |
355 // another blocking loop. We need to make sure all menus are hidden | 355 // another blocking loop. We need to make sure all menus are hidden |
356 // before that happens otherwise the menus will stay on screen. | 356 // before that happens otherwise the menus will stay on screen. |
357 CloseAllNestedMenus(); | 357 CloseAllNestedMenus(); |
358 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); | 358 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); |
359 | 359 |
360 // Set exit_all_, which makes sure all nested loops exit immediately. | 360 // Set exit_all_, which makes sure all nested loops exit immediately. |
361 if (exit_type_ != EXIT_DESTROYED) | 361 if (exit_type_ != EXIT_DESTROYED) |
362 exit_type_ = EXIT_ALL; | 362 SetExitType(EXIT_ALL); |
363 } | 363 } |
364 } | 364 } |
365 | 365 |
366 // If we stopped running because one of the menus was destroyed chances are | 366 // If we stopped running because one of the menus was destroyed chances are |
367 // the button was also destroyed. | 367 // the button was also destroyed. |
368 if (exit_type_ != EXIT_DESTROYED && menu_button_) { | 368 if (exit_type_ != EXIT_DESTROYED && menu_button_) { |
369 menu_button_->SetState(CustomButton::BS_NORMAL); | 369 menu_button_->SetState(CustomButton::BS_NORMAL); |
370 menu_button_->SchedulePaint(); | 370 menu_button_->SchedulePaint(); |
371 } | 371 } |
372 | 372 |
373 return result; | 373 return result; |
374 } | 374 } |
375 | 375 |
376 void MenuController::Cancel(ExitType type) { | 376 void MenuController::Cancel(ExitType type) { |
377 // If the menu has already been destroyed, no further cancellation is | 377 // If the menu has already been destroyed, no further cancellation is |
378 // needed. We especially don't want to set the |exit_type_| to a lesser | 378 // needed. We especially don't want to set the |exit_type_| to a lesser |
379 // value. | 379 // value. |
380 if (exit_type_ == EXIT_DESTROYED || exit_type_ == type) | 380 if (exit_type_ == EXIT_DESTROYED || exit_type_ == type) |
381 return; | 381 return; |
382 | 382 |
383 if (!showing_) { | 383 if (!showing_) { |
384 // This occurs if we're in the process of notifying the delegate for a drop | 384 // This occurs if we're in the process of notifying the delegate for a drop |
385 // and the delegate cancels us. | 385 // and the delegate cancels us. |
386 return; | 386 return; |
387 } | 387 } |
388 | 388 |
389 MenuItemView* selected = state_.item; | 389 MenuItemView* selected = state_.item; |
390 exit_type_ = type; | 390 SetExitType(type); |
391 | 391 |
392 SendMouseCaptureLostToActiveView(); | 392 SendMouseCaptureLostToActiveView(); |
393 | 393 |
394 // Hide windows immediately. | 394 // Hide windows immediately. |
395 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); | 395 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); |
396 | 396 |
397 if (!blocking_run_) { | 397 if (!blocking_run_) { |
398 // If we didn't block the caller we need to notify the menu, which | 398 // If we didn't block the caller we need to notify the menu, which |
399 // triggers deleting us. | 399 // triggers deleting us. |
400 DCHECK(selected); | 400 DCHECK(selected); |
(...skipping 18 matching lines...) Expand all Loading... |
419 return; // Ignore presses on scroll buttons. | 419 return; // Ignore presses on scroll buttons. |
420 | 420 |
421 if (part.type == MenuPart::NONE || | 421 if (part.type == MenuPart::NONE || |
422 (part.type == MenuPart::MENU_ITEM && part.menu && | 422 (part.type == MenuPart::MENU_ITEM && part.menu && |
423 part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) { | 423 part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) { |
424 // Mouse wasn't pressed over any menu, or the active menu, cancel. | 424 // Mouse wasn't pressed over any menu, or the active menu, cancel. |
425 | 425 |
426 // We're going to close and we own the mouse capture. We need to repost the | 426 // We're going to close and we own the mouse capture. We need to repost the |
427 // mouse down, otherwise the window the user clicked on won't get the | 427 // mouse down, otherwise the window the user clicked on won't get the |
428 // event. | 428 // event. |
429 #if defined(OS_WIN) | 429 #if defined(OS_WIN) && !defined(USE_AURA) |
430 RepostEvent(source, event); | 430 RepostEvent(source, event); |
431 // NOTE: not reposting on linux seems fine. | 431 // NOTE: not reposting on linux seems fine. |
432 #endif | 432 #endif |
433 | 433 |
434 // And close. | 434 // And close. |
435 ExitType exit_type = EXIT_ALL; | 435 ExitType exit_type = EXIT_ALL; |
436 if (!menu_stack_.empty()) { | 436 if (!menu_stack_.empty()) { |
437 // We're running nested menus. Only exit all if the mouse wasn't over one | 437 // We're running nested menus. Only exit all if the mouse wasn't over one |
438 // of the menus from the last run. | 438 // of the menus from the last run. |
439 gfx::Point screen_loc(event.location()); | 439 gfx::Point screen_loc(event.location()); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
713 | 713 |
714 MenuItemView* drop_target = drop_target_; | 714 MenuItemView* drop_target = drop_target_; |
715 MenuDelegate::DropPosition drop_position = drop_position_; | 715 MenuDelegate::DropPosition drop_position = drop_position_; |
716 | 716 |
717 // Close all menus, including any nested menus. | 717 // Close all menus, including any nested menus. |
718 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); | 718 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); |
719 CloseAllNestedMenus(); | 719 CloseAllNestedMenus(); |
720 | 720 |
721 // Set state such that we exit. | 721 // Set state such that we exit. |
722 showing_ = false; | 722 showing_ = false; |
723 exit_type_ = EXIT_ALL; | 723 SetExitType(EXIT_ALL); |
724 | 724 |
725 // If over an empty menu item, drop occurs on the parent. | 725 // If over an empty menu item, drop occurs on the parent. |
726 if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID) | 726 if (drop_target->id() == MenuItemView::kEmptyMenuItemViewID) |
727 drop_target = drop_target->GetParentMenuItem(); | 727 drop_target = drop_target->GetParentMenuItem(); |
728 | 728 |
729 if (!IsBlockingRun()) { | 729 if (!IsBlockingRun()) { |
730 delegate_->DropMenuClosed( | 730 delegate_->DropMenuClosed( |
731 internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE, | 731 internal::MenuControllerDelegate::DONT_NOTIFY_DELEGATE, |
732 item->GetRootMenuItem()); | 732 item->GetRootMenuItem()); |
733 } | 733 } |
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1094 // avoid repeated system queries for the info. | 1094 // avoid repeated system queries for the info. |
1095 pending_state_.monitor_bounds = gfx::Screen::GetMonitorWorkAreaNearestPoint( | 1095 pending_state_.monitor_bounds = gfx::Screen::GetMonitorWorkAreaNearestPoint( |
1096 bounds.origin()); | 1096 bounds.origin()); |
1097 } | 1097 } |
1098 | 1098 |
1099 void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { | 1099 void MenuController::Accept(MenuItemView* item, int mouse_event_flags) { |
1100 DCHECK(IsBlockingRun()); | 1100 DCHECK(IsBlockingRun()); |
1101 result_ = item; | 1101 result_ = item; |
1102 if (item && !menu_stack_.empty() && | 1102 if (item && !menu_stack_.empty() && |
1103 !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) { | 1103 !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) { |
1104 exit_type_ = EXIT_OUTERMOST; | 1104 SetExitType(EXIT_OUTERMOST); |
1105 } else { | 1105 } else { |
1106 exit_type_ = EXIT_ALL; | 1106 SetExitType(EXIT_ALL); |
1107 } | 1107 } |
1108 result_mouse_event_flags_ = mouse_event_flags; | 1108 result_mouse_event_flags_ = mouse_event_flags; |
1109 } | 1109 } |
1110 | 1110 |
1111 bool MenuController::ShowSiblingMenu(SubmenuView* source, | 1111 bool MenuController::ShowSiblingMenu(SubmenuView* source, |
1112 const MouseEvent& event) { | 1112 const MouseEvent& event) { |
1113 if (!menu_stack_.empty() || !menu_button_) | 1113 if (!menu_stack_.empty() || !menu_button_) |
1114 return false; | 1114 return false; |
1115 | 1115 |
1116 View* source_view = source->GetScrollViewContainer(); | 1116 View* source_view = source->GetScrollViewContainer(); |
(...skipping 658 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1775 } | 1775 } |
1776 | 1776 |
1777 // If no mnemonics found, look at first character of titles. | 1777 // If no mnemonics found, look at first character of titles. |
1778 details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); | 1778 details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic); |
1779 if (details.first_match != -1) | 1779 if (details.first_match != -1) |
1780 return AcceptOrSelect(item, details); | 1780 return AcceptOrSelect(item, details); |
1781 | 1781 |
1782 return false; | 1782 return false; |
1783 } | 1783 } |
1784 | 1784 |
1785 #if defined(OS_WIN) | 1785 #if defined(OS_WIN) && !defined(USE_AURA) |
1786 #if defined(USE_AURA) | |
1787 void MenuController::RepostEvent(SubmenuView* source, | |
1788 const MouseEvent& event) { | |
1789 } | |
1790 #else | |
1791 void MenuController::RepostEvent(SubmenuView* source, | 1786 void MenuController::RepostEvent(SubmenuView* source, |
1792 const MouseEvent& event) { | 1787 const MouseEvent& event) { |
1793 if (!state_.item) { | 1788 if (!state_.item) { |
1794 // We some times get an event after closing all the menus. Ignore it. | 1789 // We some times get an event after closing all the menus. Ignore it. |
1795 // Make sure the menu is in fact not visible. If the menu is visible, then | 1790 // Make sure the menu is in fact not visible. If the menu is visible, then |
1796 // we're in a bad state where we think the menu isn't visibile but it is. | 1791 // we're in a bad state where we think the menu isn't visibile but it is. |
1797 DCHECK(!source->GetWidget()->IsVisible()); | 1792 DCHECK(!source->GetWidget()->IsVisible()); |
1798 return; | 1793 return; |
1799 } | 1794 } |
1800 | 1795 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1847 if (in_client_area) { | 1842 if (in_client_area) { |
1848 PostMessage(window, event_type, event.native_event().wParam, | 1843 PostMessage(window, event_type, event.native_event().wParam, |
1849 MAKELPARAM(window_x, window_y)); | 1844 MAKELPARAM(window_x, window_y)); |
1850 } else { | 1845 } else { |
1851 PostMessage(window, event_type, nc_hit_result, | 1846 PostMessage(window, event_type, nc_hit_result, |
1852 MAKELPARAM(screen_loc.x(), screen_loc.y())); | 1847 MAKELPARAM(screen_loc.x(), screen_loc.y())); |
1853 } | 1848 } |
1854 } | 1849 } |
1855 } | 1850 } |
1856 } | 1851 } |
1857 #endif // !defined(USE_AURA) | |
1858 #endif // defined(OS_WIN) | 1852 #endif // defined(OS_WIN) |
1859 | 1853 |
1860 void MenuController::SetDropMenuItem( | 1854 void MenuController::SetDropMenuItem( |
1861 MenuItemView* new_target, | 1855 MenuItemView* new_target, |
1862 MenuDelegate::DropPosition new_position) { | 1856 MenuDelegate::DropPosition new_position) { |
1863 if (new_target == drop_target_ && new_position == drop_position_) | 1857 if (new_target == drop_target_ && new_position == drop_position_) |
1864 return; | 1858 return; |
1865 | 1859 |
1866 if (drop_target_) { | 1860 if (drop_target_) { |
1867 drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem( | 1861 drop_target_->GetParentMenuItem()->GetSubmenu()->SetDropMenuItem( |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1954 if (!active_mouse_view_) | 1948 if (!active_mouse_view_) |
1955 return; | 1949 return; |
1956 | 1950 |
1957 // Reset the active_mouse_view_ before sending mouse capture lost. That way if | 1951 // Reset the active_mouse_view_ before sending mouse capture lost. That way if |
1958 // it calls back to us, we aren't in a weird state. | 1952 // it calls back to us, we aren't in a weird state. |
1959 View* active_view = active_mouse_view_; | 1953 View* active_view = active_mouse_view_; |
1960 active_mouse_view_ = NULL; | 1954 active_mouse_view_ = NULL; |
1961 active_view->OnMouseCaptureLost(); | 1955 active_view->OnMouseCaptureLost(); |
1962 } | 1956 } |
1963 | 1957 |
| 1958 void MenuController::SetExitType(ExitType type) { |
| 1959 exit_type_ = type; |
| 1960 #if defined(USE_AURA) |
| 1961 // On aura, closing menu may not trigger next native event, which |
| 1962 // is necessary to exit from nested loop (See Dispatch methods). |
| 1963 // Send non-op event so that Dispatch method will always be called. |
| 1964 // crbug.com/104684. |
| 1965 if (exit_type_ == EXIT_ALL || exit_type_ == EXIT_DESTROYED) |
| 1966 aura::Desktop::GetInstance()->PostNativeEvent(ui::CreateNoopEvent()); |
| 1967 #endif |
| 1968 } |
| 1969 |
| 1970 |
1964 } // namespace views | 1971 } // namespace views |
OLD | NEW |