| 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 |