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 |