| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ui/views/controls/menu/menu_controller.h" | 5 #include "ui/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/strings/utf_string_conversions.h" | 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "base/time/time.h" | 10 #include "base/time/time.h" |
| (...skipping 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 } | 318 } |
| 319 } | 319 } |
| 320 | 320 |
| 321 bool nested_menu = showing_; | 321 bool nested_menu = showing_; |
| 322 if (showing_) { | 322 if (showing_) { |
| 323 // Only support nesting of blocking_run menus, nesting of | 323 // Only support nesting of blocking_run menus, nesting of |
| 324 // blocking/non-blocking shouldn't be needed. | 324 // blocking/non-blocking shouldn't be needed. |
| 325 DCHECK(blocking_run_); | 325 DCHECK(blocking_run_); |
| 326 | 326 |
| 327 // We're already showing, push the current state. | 327 // We're already showing, push the current state. |
| 328 menu_stack_.push_back(state_); | 328 menu_stack_.push_back( |
| 329 std::make_pair(state_, make_linked_ptr(pressed_lock_.release()))); |
| 329 | 330 |
| 330 // The context menu should be owned by the same parent. | 331 // The context menu should be owned by the same parent. |
| 331 DCHECK_EQ(owner_, parent); | 332 DCHECK_EQ(owner_, parent); |
| 332 } else { | 333 } else { |
| 333 showing_ = true; | 334 showing_ = true; |
| 334 } | 335 } |
| 335 | 336 |
| 336 // Reset current state. | 337 // Reset current state. |
| 337 pending_state_ = State(); | 338 pending_state_ = State(); |
| 338 state_ = State(); | 339 state_ = State(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 350 if (!blocking_run_) { | 351 if (!blocking_run_) { |
| 351 if (!is_nested_drag) { | 352 if (!is_nested_drag) { |
| 352 // Start the timer to hide the menu. This is needed as we get no | 353 // Start the timer to hide the menu. This is needed as we get no |
| 353 // notification when the drag has finished. | 354 // notification when the drag has finished. |
| 354 StartCancelAllTimer(); | 355 StartCancelAllTimer(); |
| 355 } | 356 } |
| 356 return NULL; | 357 return NULL; |
| 357 } | 358 } |
| 358 | 359 |
| 359 if (button) | 360 if (button) |
| 360 menu_button_ = button; | 361 pressed_lock_.reset(new MenuButton::PressedLock(button)); |
| 361 | 362 |
| 362 // Make sure Chrome doesn't attempt to shut down while the menu is showing. | 363 // Make sure Chrome doesn't attempt to shut down while the menu is showing. |
| 363 if (ViewsDelegate::views_delegate) | 364 if (ViewsDelegate::views_delegate) |
| 364 ViewsDelegate::views_delegate->AddRef(); | 365 ViewsDelegate::views_delegate->AddRef(); |
| 365 | 366 |
| 366 // We need to turn on nestable tasks as in some situations (pressing alt-f for | 367 // We need to turn on nestable tasks as in some situations (pressing alt-f for |
| 367 // one) the menus are run from a task. If we don't do this and are invoked | 368 // one) the menus are run from a task. If we don't do this and are invoked |
| 368 // from a task none of the tasks we schedule are processed and the menu | 369 // from a task none of the tasks we schedule are processed and the menu |
| 369 // appears totally broken. | 370 // appears totally broken. |
| 370 message_loop_depth_++; | 371 message_loop_depth_++; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 392 ::GetCursorPos(&cursor_pos); | 393 ::GetCursorPos(&cursor_pos); |
| 393 HWND window = ::WindowFromPoint(cursor_pos); | 394 HWND window = ::WindowFromPoint(cursor_pos); |
| 394 if (::GetWindowThreadProcessId(window, NULL) == | 395 if (::GetWindowThreadProcessId(window, NULL) == |
| 395 ::GetCurrentThreadId()) { | 396 ::GetCurrentThreadId()) { |
| 396 ::SetProp(window, ui::kIgnoreTouchMouseActivateForWindow, | 397 ::SetProp(window, ui::kIgnoreTouchMouseActivateForWindow, |
| 397 reinterpret_cast<HANDLE>(true)); | 398 reinterpret_cast<HANDLE>(true)); |
| 398 } | 399 } |
| 399 } | 400 } |
| 400 #endif | 401 #endif |
| 401 | 402 |
| 403 linked_ptr<MenuButton::PressedLock> nested_pressed_lock; |
| 402 if (nested_menu) { | 404 if (nested_menu) { |
| 403 DCHECK(!menu_stack_.empty()); | 405 DCHECK(!menu_stack_.empty()); |
| 404 // We're running from within a menu, restore the previous state. | 406 // We're running from within a menu, restore the previous state. |
| 405 // The menus are already showing, so we don't have to show them. | 407 // The menus are already showing, so we don't have to show them. |
| 406 state_ = menu_stack_.back(); | 408 state_ = menu_stack_.back().first; |
| 407 pending_state_ = menu_stack_.back(); | 409 pending_state_ = menu_stack_.back().first; |
| 410 nested_pressed_lock = menu_stack_.back().second; |
| 408 menu_stack_.pop_back(); | 411 menu_stack_.pop_back(); |
| 409 } else { | 412 } else { |
| 410 showing_ = false; | 413 showing_ = false; |
| 411 did_capture_ = false; | 414 did_capture_ = false; |
| 412 } | 415 } |
| 413 | 416 |
| 414 MenuItemView* result = result_; | 417 MenuItemView* result = result_; |
| 415 // In case we're nested, reset result_. | 418 // In case we're nested, reset result_. |
| 416 result_ = NULL; | 419 result_ = NULL; |
| 417 | 420 |
| 418 if (result_event_flags) | 421 if (result_event_flags) |
| 419 *result_event_flags = accept_event_flags_; | 422 *result_event_flags = accept_event_flags_; |
| 420 | 423 |
| 421 if (exit_type_ == EXIT_OUTERMOST) { | 424 if (exit_type_ == EXIT_OUTERMOST) { |
| 422 SetExitType(EXIT_NONE); | 425 SetExitType(EXIT_NONE); |
| 423 } else { | 426 } else { |
| 424 if (nested_menu && result) { | 427 if (nested_menu && result) { |
| 425 // We're nested and about to return a value. The caller might enter | 428 // We're nested and about to return a value. The caller might enter |
| 426 // another blocking loop. We need to make sure all menus are hidden | 429 // another blocking loop. We need to make sure all menus are hidden |
| 427 // before that happens otherwise the menus will stay on screen. | 430 // before that happens otherwise the menus will stay on screen. |
| 428 CloseAllNestedMenus(); | 431 CloseAllNestedMenus(); |
| 429 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); | 432 SetSelection(NULL, SELECTION_UPDATE_IMMEDIATELY | SELECTION_EXIT); |
| 430 | 433 |
| 431 // Set exit_all_, which makes sure all nested loops exit immediately. | 434 // Set exit_all_, which makes sure all nested loops exit immediately. |
| 432 if (exit_type_ != EXIT_DESTROYED) | 435 if (exit_type_ != EXIT_DESTROYED) |
| 433 SetExitType(EXIT_ALL); | 436 SetExitType(EXIT_ALL); |
| 434 } | 437 } |
| 435 } | 438 } |
| 436 | 439 |
| 437 // If we stopped running because one of the menus was destroyed chances are | 440 // Reset our pressed lock to the previous state's, if there was one. |
| 438 // the button was also destroyed. | 441 // The lock handles the case if the button was destroyed. |
| 439 if (exit_type_ != EXIT_DESTROYED && menu_button_) { | 442 pressed_lock_.reset(nested_pressed_lock.release()); |
| 440 menu_button_->SetState(CustomButton::STATE_NORMAL); | 443 |
| 441 menu_button_->SchedulePaint(); | |
| 442 } | |
| 443 return result; | 444 return result; |
| 444 } | 445 } |
| 445 | 446 |
| 446 void MenuController::Cancel(ExitType type) { | 447 void MenuController::Cancel(ExitType type) { |
| 447 // If the menu has already been destroyed, no further cancellation is | 448 // If the menu has already been destroyed, no further cancellation is |
| 448 // needed. We especially don't want to set the |exit_type_| to a lesser | 449 // needed. We especially don't want to set the |exit_type_| to a lesser |
| 449 // value. | 450 // value. |
| 450 if (exit_type_ == EXIT_DESTROYED || exit_type_ == type) | 451 if (exit_type_ == EXIT_DESTROYED || exit_type_ == type) |
| 451 return; | 452 return; |
| 452 | 453 |
| (...skipping 473 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 926 #endif | 927 #endif |
| 927 | 928 |
| 928 // And close. | 929 // And close. |
| 929 ExitType exit_type = EXIT_ALL; | 930 ExitType exit_type = EXIT_ALL; |
| 930 if (!menu_stack_.empty()) { | 931 if (!menu_stack_.empty()) { |
| 931 // We're running nested menus. Only exit all if the mouse wasn't over one | 932 // We're running nested menus. Only exit all if the mouse wasn't over one |
| 932 // of the menus from the last run. | 933 // of the menus from the last run. |
| 933 gfx::Point screen_loc(event.location()); | 934 gfx::Point screen_loc(event.location()); |
| 934 View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc); | 935 View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc); |
| 935 MenuPart last_part = GetMenuPartByScreenCoordinateUsingMenu( | 936 MenuPart last_part = GetMenuPartByScreenCoordinateUsingMenu( |
| 936 menu_stack_.back().item, screen_loc); | 937 menu_stack_.back().first.item, screen_loc); |
| 937 if (last_part.type != MenuPart::NONE) | 938 if (last_part.type != MenuPart::NONE) |
| 938 exit_type = EXIT_OUTERMOST; | 939 exit_type = EXIT_OUTERMOST; |
| 939 } | 940 } |
| 940 Cancel(exit_type); | 941 Cancel(exit_type); |
| 941 | 942 |
| 942 #if defined(OS_CHROMEOS) | 943 #if defined(OS_CHROMEOS) |
| 943 // We're going to exit the menu and want to repost the event so that is | 944 // We're going to exit the menu and want to repost the event so that is |
| 944 // is handled normally after the context menu has exited. We call | 945 // is handled normally after the context menu has exited. We call |
| 945 // RepostEvent after Cancel so that mouse capture has been released so | 946 // RepostEvent after Cancel so that mouse capture has been released so |
| 946 // that finding the event target is unaffected by the current capture. | 947 // that finding the event target is unaffected by the current capture. |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1085 accept_event_flags_(0), | 1086 accept_event_flags_(0), |
| 1086 drop_target_(NULL), | 1087 drop_target_(NULL), |
| 1087 drop_position_(MenuDelegate::DROP_UNKNOWN), | 1088 drop_position_(MenuDelegate::DROP_UNKNOWN), |
| 1088 owner_(NULL), | 1089 owner_(NULL), |
| 1089 possible_drag_(false), | 1090 possible_drag_(false), |
| 1090 drag_in_progress_(false), | 1091 drag_in_progress_(false), |
| 1091 did_initiate_drag_(false), | 1092 did_initiate_drag_(false), |
| 1092 valid_drop_coordinates_(false), | 1093 valid_drop_coordinates_(false), |
| 1093 last_drop_operation_(MenuDelegate::DROP_UNKNOWN), | 1094 last_drop_operation_(MenuDelegate::DROP_UNKNOWN), |
| 1094 showing_submenu_(false), | 1095 showing_submenu_(false), |
| 1095 menu_button_(NULL), | |
| 1096 active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()), | 1096 active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()), |
| 1097 delegate_(delegate), | 1097 delegate_(delegate), |
| 1098 message_loop_depth_(0), | 1098 message_loop_depth_(0), |
| 1099 menu_config_(theme), | 1099 menu_config_(theme), |
| 1100 closing_event_time_(base::TimeDelta()), | 1100 closing_event_time_(base::TimeDelta()), |
| 1101 menu_start_time_(base::TimeTicks()), | 1101 menu_start_time_(base::TimeTicks()), |
| 1102 is_combobox_(false), | 1102 is_combobox_(false), |
| 1103 item_selected_by_touch_(false), | 1103 item_selected_by_touch_(false), |
| 1104 message_loop_(MenuMessageLoop::Create()) { | 1104 message_loop_(MenuMessageLoop::Create()) { |
| 1105 active_instance_ = this; | 1105 active_instance_ = this; |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1176 !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) { | 1176 !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) { |
| 1177 SetExitType(EXIT_OUTERMOST); | 1177 SetExitType(EXIT_OUTERMOST); |
| 1178 } else { | 1178 } else { |
| 1179 SetExitType(EXIT_ALL); | 1179 SetExitType(EXIT_ALL); |
| 1180 } | 1180 } |
| 1181 accept_event_flags_ = event_flags; | 1181 accept_event_flags_ = event_flags; |
| 1182 } | 1182 } |
| 1183 | 1183 |
| 1184 bool MenuController::ShowSiblingMenu(SubmenuView* source, | 1184 bool MenuController::ShowSiblingMenu(SubmenuView* source, |
| 1185 const gfx::Point& mouse_location) { | 1185 const gfx::Point& mouse_location) { |
| 1186 if (!menu_stack_.empty() || !menu_button_) | 1186 if (!menu_stack_.empty() || !pressed_lock_.get()) |
| 1187 return false; | 1187 return false; |
| 1188 | 1188 |
| 1189 View* source_view = source->GetScrollViewContainer(); | 1189 View* source_view = source->GetScrollViewContainer(); |
| 1190 if (mouse_location.x() >= 0 && | 1190 if (mouse_location.x() >= 0 && |
| 1191 mouse_location.x() < source_view->width() && | 1191 mouse_location.x() < source_view->width() && |
| 1192 mouse_location.y() >= 0 && | 1192 mouse_location.y() >= 0 && |
| 1193 mouse_location.y() < source_view->height()) { | 1193 mouse_location.y() < source_view->height()) { |
| 1194 // The mouse is over the menu, no need to continue. | 1194 // The mouse is over the menu, no need to continue. |
| 1195 return false; | 1195 return false; |
| 1196 } | 1196 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1216 delegate_->SiblingMenuCreated(alt_menu); | 1216 delegate_->SiblingMenuCreated(alt_menu); |
| 1217 | 1217 |
| 1218 if (!button) { | 1218 if (!button) { |
| 1219 // If the delegate returns a menu, they must also return a button. | 1219 // If the delegate returns a menu, they must also return a button. |
| 1220 NOTREACHED(); | 1220 NOTREACHED(); |
| 1221 return false; | 1221 return false; |
| 1222 } | 1222 } |
| 1223 | 1223 |
| 1224 // There is a sibling menu, update the button state, hide the current menu | 1224 // There is a sibling menu, update the button state, hide the current menu |
| 1225 // and show the new one. | 1225 // and show the new one. |
| 1226 menu_button_->SetState(CustomButton::STATE_NORMAL); | 1226 pressed_lock_.reset(new MenuButton::PressedLock(button)); |
| 1227 menu_button_->SchedulePaint(); | |
| 1228 menu_button_ = button; | |
| 1229 menu_button_->SetState(CustomButton::STATE_PRESSED); | |
| 1230 menu_button_->SchedulePaint(); | |
| 1231 | 1227 |
| 1232 // Need to reset capture when we show the menu again, otherwise we aren't | 1228 // Need to reset capture when we show the menu again, otherwise we aren't |
| 1233 // going to get any events. | 1229 // going to get any events. |
| 1234 did_capture_ = false; | 1230 did_capture_ = false; |
| 1235 gfx::Point screen_menu_loc; | 1231 gfx::Point screen_menu_loc; |
| 1236 View::ConvertPointToScreen(button, &screen_menu_loc); | 1232 View::ConvertPointToScreen(button, &screen_menu_loc); |
| 1237 | 1233 |
| 1238 // It is currently not possible to show a submenu recursively in a bubble. | 1234 // It is currently not possible to show a submenu recursively in a bubble. |
| 1239 DCHECK(!MenuItemView::IsBubble(anchor)); | 1235 DCHECK(!MenuItemView::IsBubble(anchor)); |
| 1240 // Subtract 1 from the height to make the popup flush with the button border. | 1236 // Subtract 1 from the height to make the popup flush with the button border. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1264 | 1260 |
| 1265 if (menu_item->GetDelegate()->ShowContextMenu( | 1261 if (menu_item->GetDelegate()->ShowContextMenu( |
| 1266 menu_item, menu_item->GetCommand(), loc, source_type)) { | 1262 menu_item, menu_item->GetCommand(), loc, source_type)) { |
| 1267 SendMouseCaptureLostToActiveView(); | 1263 SendMouseCaptureLostToActiveView(); |
| 1268 return true; | 1264 return true; |
| 1269 } | 1265 } |
| 1270 return false; | 1266 return false; |
| 1271 } | 1267 } |
| 1272 | 1268 |
| 1273 void MenuController::CloseAllNestedMenus() { | 1269 void MenuController::CloseAllNestedMenus() { |
| 1274 for (std::list<State>::iterator i = menu_stack_.begin(); | 1270 for (std::list<NestedState>::iterator i = menu_stack_.begin(); |
| 1275 i != menu_stack_.end(); ++i) { | 1271 i != menu_stack_.end(); ++i) { |
| 1276 MenuItemView* last_item = i->item; | 1272 State& state = i->first; |
| 1273 MenuItemView* last_item = state.item; |
| 1277 for (MenuItemView* item = last_item; item; | 1274 for (MenuItemView* item = last_item; item; |
| 1278 item = item->GetParentMenuItem()) { | 1275 item = item->GetParentMenuItem()) { |
| 1279 CloseMenu(item); | 1276 CloseMenu(item); |
| 1280 last_item = item; | 1277 last_item = item; |
| 1281 } | 1278 } |
| 1282 i->submenu_open = false; | 1279 state.submenu_open = false; |
| 1283 i->item = last_item; | 1280 state.item = last_item; |
| 1284 } | 1281 } |
| 1285 } | 1282 } |
| 1286 | 1283 |
| 1287 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) { | 1284 MenuItemView* MenuController::GetMenuItemAt(View* source, int x, int y) { |
| 1288 // Walk the view hierarchy until we find a menu item (or the root). | 1285 // Walk the view hierarchy until we find a menu item (or the root). |
| 1289 View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y)); | 1286 View* child_under_mouse = source->GetEventHandlerForPoint(gfx::Point(x, y)); |
| 1290 while (child_under_mouse && | 1287 while (child_under_mouse && |
| 1291 child_under_mouse->id() != MenuItemView::kMenuItemViewID) { | 1288 child_under_mouse->id() != MenuItemView::kMenuItemViewID) { |
| 1292 child_under_mouse = child_under_mouse->parent(); | 1289 child_under_mouse = child_under_mouse->parent(); |
| 1293 } | 1290 } |
| (...skipping 1015 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2309 } | 2306 } |
| 2310 } | 2307 } |
| 2311 | 2308 |
| 2312 gfx::Screen* MenuController::GetScreen() { | 2309 gfx::Screen* MenuController::GetScreen() { |
| 2313 Widget* root = owner_ ? owner_->GetTopLevelWidget() : NULL; | 2310 Widget* root = owner_ ? owner_->GetTopLevelWidget() : NULL; |
| 2314 return root ? gfx::Screen::GetScreenFor(root->GetNativeView()) | 2311 return root ? gfx::Screen::GetScreenFor(root->GetNativeView()) |
| 2315 : gfx::Screen::GetNativeScreen(); | 2312 : gfx::Screen::GetNativeScreen(); |
| 2316 } | 2313 } |
| 2317 | 2314 |
| 2318 } // namespace views | 2315 } // namespace views |
| OLD | NEW |