| 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_item_view.h" | 5 #include "views/controls/menu/menu_item_view.h" |
| 6 | 6 |
| 7 #include "base/i18n/case_conversion.h" | 7 #include "base/i18n/case_conversion.h" |
| 8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
| 9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
| 10 #include "grit/ui_strings.h" | 10 #include "grit/ui_strings.h" |
| 11 #include "ui/base/accessibility/accessible_view_state.h" | 11 #include "ui/base/accessibility/accessible_view_state.h" |
| 12 #include "ui/base/l10n/l10n_util.h" | 12 #include "ui/base/l10n/l10n_util.h" |
| 13 #include "ui/base/models/menu_model.h" | 13 #include "ui/base/models/menu_model.h" |
| 14 #include "ui/gfx/canvas.h" | 14 #include "ui/gfx/canvas.h" |
| 15 #include "views/controls/button/menu_button.h" | 15 #include "views/controls/button/menu_button.h" |
| 16 #include "views/controls/button/text_button.h" | 16 #include "views/controls/button/text_button.h" |
| 17 #include "views/controls/menu/menu_config.h" | 17 #include "views/controls/menu/menu_config.h" |
| 18 #include "views/controls/menu/menu_controller.h" | 18 #include "views/controls/menu/menu_controller.h" |
| 19 #include "views/controls/menu/menu_separator.h" | 19 #include "views/controls/menu/menu_separator.h" |
| 20 #include "views/controls/menu/submenu_view.h" | 20 #include "views/controls/menu/submenu_view.h" |
| 21 | 21 |
| 22 #if defined(OS_WIN) | |
| 23 #include "base/win/win_util.h" | |
| 24 #endif | |
| 25 | |
| 26 namespace views { | 22 namespace views { |
| 27 | 23 |
| 28 namespace { | 24 namespace { |
| 29 | 25 |
| 30 // EmptyMenuMenuItem --------------------------------------------------------- | 26 // EmptyMenuMenuItem --------------------------------------------------------- |
| 31 | 27 |
| 32 // EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem | 28 // EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem |
| 33 // is itself a MenuItemView, but it uses a different ID so that it isn't | 29 // is itself a MenuItemView, but it uses a different ID so that it isn't |
| 34 // identified as a MenuItemView. | 30 // identified as a MenuItemView. |
| 35 | 31 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 has_icons_(false), | 89 has_icons_(false), |
| 94 top_margin_(-1), | 90 top_margin_(-1), |
| 95 bottom_margin_(-1), | 91 bottom_margin_(-1), |
| 96 requested_menu_position_(POSITION_BEST_FIT), | 92 requested_menu_position_(POSITION_BEST_FIT), |
| 97 actual_menu_position_(requested_menu_position_) { | 93 actual_menu_position_(requested_menu_position_) { |
| 98 // NOTE: don't check the delegate for NULL, UpdateMenuPartSizes supplies a | 94 // NOTE: don't check the delegate for NULL, UpdateMenuPartSizes supplies a |
| 99 // NULL delegate. | 95 // NULL delegate. |
| 100 Init(NULL, 0, SUBMENU, delegate); | 96 Init(NULL, 0, SUBMENU, delegate); |
| 101 } | 97 } |
| 102 | 98 |
| 103 MenuItemView::~MenuItemView() { | |
| 104 // TODO(sky): ownership is bit wrong now. In particular if a nested message | |
| 105 // loop is running deletion can't be done, otherwise the stack gets | |
| 106 // thoroughly screwed. The destructor should be made private, and | |
| 107 // MenuController should be the only place handling deletion of the menu. | |
| 108 // (57890). | |
| 109 delete submenu_; | |
| 110 STLDeleteElements(&removed_items_); | |
| 111 } | |
| 112 | |
| 113 void MenuItemView::ChildPreferredSizeChanged(View* child) { | 99 void MenuItemView::ChildPreferredSizeChanged(View* child) { |
| 114 pref_size_.SetSize(0, 0); | 100 pref_size_.SetSize(0, 0); |
| 115 PreferredSizeChanged(); | 101 PreferredSizeChanged(); |
| 116 } | 102 } |
| 117 | 103 |
| 118 bool MenuItemView::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { | 104 bool MenuItemView::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { |
| 119 *tooltip = UTF16ToWideHack(tooltip_); | 105 *tooltip = UTF16ToWideHack(tooltip_); |
| 120 if (!tooltip->empty()) | 106 if (!tooltip->empty()) |
| 121 return true; | 107 return true; |
| 122 | 108 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 | 171 |
| 186 // Append accelerator text. | 172 // Append accelerator text. |
| 187 if (!accelerator_text.empty()) { | 173 if (!accelerator_text.empty()) { |
| 188 accessible_name.push_back(' '); | 174 accessible_name.push_back(' '); |
| 189 accessible_name.append(accelerator_text); | 175 accessible_name.append(accelerator_text); |
| 190 } | 176 } |
| 191 | 177 |
| 192 return accessible_name; | 178 return accessible_name; |
| 193 } | 179 } |
| 194 | 180 |
| 195 void MenuItemView::RunMenuAt(Widget* parent, | |
| 196 MenuButton* button, | |
| 197 const gfx::Rect& bounds, | |
| 198 AnchorPosition anchor, | |
| 199 bool has_mnemonics) { | |
| 200 // Show mnemonics if the button has focus or alt is pressed. | |
| 201 bool show_mnemonics = button ? button->HasFocus() : false; | |
| 202 #if defined(OS_WIN) | |
| 203 // We don't currently need this on gtk as showing the menu gives focus to the | |
| 204 // button first. | |
| 205 if (!show_mnemonics) | |
| 206 show_mnemonics = base::win::IsAltPressed(); | |
| 207 #endif | |
| 208 PrepareForRun(has_mnemonics, show_mnemonics); | |
| 209 int mouse_event_flags; | |
| 210 | |
| 211 MenuController* controller = MenuController::GetActiveInstance(); | |
| 212 if (controller && !controller->IsBlockingRun()) { | |
| 213 // A menu is already showing, but it isn't a blocking menu. Cancel it. | |
| 214 // We can get here during drag and drop if the user right clicks on the | |
| 215 // menu quickly after the drop. | |
| 216 controller->Cancel(MenuController::EXIT_ALL); | |
| 217 controller = NULL; | |
| 218 } | |
| 219 // TODO(sky): remove volatile, used in tracking 90860. | |
| 220 volatile bool owns_controller = false; | |
| 221 if (!controller) { | |
| 222 // No menus are showing, show one. | |
| 223 controller = new MenuController(true); | |
| 224 MenuController::SetActiveInstance(controller); | |
| 225 owns_controller = true; | |
| 226 } else { | |
| 227 // A menu is already showing, use the same controller. | |
| 228 | |
| 229 // Don't support blocking from within non-blocking. | |
| 230 DCHECK(controller->IsBlockingRun()); | |
| 231 } | |
| 232 | |
| 233 controller_ = controller; | |
| 234 | |
| 235 // Run the loop. | |
| 236 MenuItemView* result = | |
| 237 controller->Run(parent, button, this, bounds, anchor, &mouse_event_flags); | |
| 238 | |
| 239 RemoveEmptyMenus(); | |
| 240 | |
| 241 controller_ = NULL; | |
| 242 | |
| 243 if (owns_controller) { | |
| 244 // We created the controller and need to delete it. | |
| 245 if (MenuController::GetActiveInstance() == controller) | |
| 246 MenuController::SetActiveInstance(NULL); | |
| 247 delete controller; | |
| 248 } | |
| 249 // Make sure all the windows we created to show the menus have been | |
| 250 // destroyed. | |
| 251 DestroyAllMenuHosts(); | |
| 252 if (result && delegate_) | |
| 253 delegate_->ExecuteCommand(result->GetCommand(), mouse_event_flags); | |
| 254 } | |
| 255 | |
| 256 void MenuItemView::RunMenuForDropAt(Widget* parent, | |
| 257 const gfx::Rect& bounds, | |
| 258 AnchorPosition anchor) { | |
| 259 PrepareForRun(false, false); | |
| 260 | |
| 261 // If there is a menu, hide it so that only one menu is shown during dnd. | |
| 262 MenuController* current_controller = MenuController::GetActiveInstance(); | |
| 263 if (current_controller) { | |
| 264 current_controller->Cancel(MenuController::EXIT_ALL); | |
| 265 } | |
| 266 | |
| 267 // Always create a new controller for non-blocking. | |
| 268 controller_ = new MenuController(false); | |
| 269 | |
| 270 // Set the instance, that way it can be canceled by another menu. | |
| 271 MenuController::SetActiveInstance(controller_); | |
| 272 | |
| 273 controller_->Run(parent, NULL, this, bounds, anchor, NULL); | |
| 274 } | |
| 275 | |
| 276 void MenuItemView::Cancel() { | 181 void MenuItemView::Cancel() { |
| 277 if (controller_ && !canceled_) { | 182 if (controller_ && !canceled_) { |
| 278 canceled_ = true; | 183 canceled_ = true; |
| 279 controller_->Cancel(MenuController::EXIT_ALL); | 184 controller_->Cancel(MenuController::EXIT_ALL); |
| 280 } | 185 } |
| 281 } | 186 } |
| 282 | 187 |
| 283 MenuItemView* MenuItemView::AddMenuItemAt(int index, | 188 MenuItemView* MenuItemView::AddMenuItemAt(int index, |
| 284 int item_id, | 189 int item_id, |
| 285 const std::wstring& label, | 190 const std::wstring& label, |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 has_mnemonics_(false), | 457 has_mnemonics_(false), |
| 553 show_mnemonics_(false), | 458 show_mnemonics_(false), |
| 554 has_icons_(false), | 459 has_icons_(false), |
| 555 top_margin_(-1), | 460 top_margin_(-1), |
| 556 bottom_margin_(-1), | 461 bottom_margin_(-1), |
| 557 requested_menu_position_(POSITION_BEST_FIT), | 462 requested_menu_position_(POSITION_BEST_FIT), |
| 558 actual_menu_position_(requested_menu_position_) { | 463 actual_menu_position_(requested_menu_position_) { |
| 559 Init(parent, command, type, NULL); | 464 Init(parent, command, type, NULL); |
| 560 } | 465 } |
| 561 | 466 |
| 467 MenuItemView::~MenuItemView() { |
| 468 delete submenu_; |
| 469 STLDeleteElements(&removed_items_); |
| 470 } |
| 471 |
| 562 std::string MenuItemView::GetClassName() const { | 472 std::string MenuItemView::GetClassName() const { |
| 563 return kViewClassName; | 473 return kViewClassName; |
| 564 } | 474 } |
| 565 | 475 |
| 566 // Calculates all sizes that we can from the OS. | 476 // Calculates all sizes that we can from the OS. |
| 567 // | 477 // |
| 568 // This is invoked prior to Running a menu. | 478 // This is invoked prior to Running a menu. |
| 569 void MenuItemView::UpdateMenuPartSizes(bool has_icons) { | 479 void MenuItemView::UpdateMenuPartSizes(bool has_icons) { |
| 570 MenuConfig::Reset(); | 480 MenuConfig::Reset(); |
| 571 const MenuConfig& config = MenuConfig::instance(); | 481 const MenuConfig& config = MenuConfig::instance(); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 606 set_id(kMenuItemViewID); | 516 set_id(kMenuItemViewID); |
| 607 has_icons_ = false; | 517 has_icons_ = false; |
| 608 | 518 |
| 609 // Don't request enabled status from the root menu item as it is just | 519 // Don't request enabled status from the root menu item as it is just |
| 610 // a container for real items. EMPTY items will be disabled. | 520 // a container for real items. EMPTY items will be disabled. |
| 611 MenuDelegate* root_delegate = GetDelegate(); | 521 MenuDelegate* root_delegate = GetDelegate(); |
| 612 if (parent && type != EMPTY && root_delegate) | 522 if (parent && type != EMPTY && root_delegate) |
| 613 SetEnabled(root_delegate->IsCommandEnabled(command)); | 523 SetEnabled(root_delegate->IsCommandEnabled(command)); |
| 614 } | 524 } |
| 615 | 525 |
| 616 void MenuItemView::DropMenuClosed(bool notify_delegate) { | |
| 617 DCHECK(controller_); | |
| 618 DCHECK(!controller_->IsBlockingRun()); | |
| 619 if (MenuController::GetActiveInstance() == controller_) | |
| 620 MenuController::SetActiveInstance(NULL); | |
| 621 delete controller_; | |
| 622 controller_ = NULL; | |
| 623 | |
| 624 RemoveEmptyMenus(); | |
| 625 | |
| 626 if (notify_delegate && delegate_) { | |
| 627 // Our delegate is null when invoked from the destructor. | |
| 628 delegate_->DropMenuClosed(this); | |
| 629 } | |
| 630 // WARNING: its possible the delegate deleted us at this point. | |
| 631 } | |
| 632 | |
| 633 void MenuItemView::PrepareForRun(bool has_mnemonics, bool show_mnemonics) { | 526 void MenuItemView::PrepareForRun(bool has_mnemonics, bool show_mnemonics) { |
| 634 // Currently we only support showing the root. | 527 // Currently we only support showing the root. |
| 635 DCHECK(!parent_menu_item_); | 528 DCHECK(!parent_menu_item_); |
| 636 | 529 |
| 637 // Force us to have a submenu. | 530 // Force us to have a submenu. |
| 638 CreateSubmenu(); | 531 CreateSubmenu(); |
| 639 actual_menu_position_ = requested_menu_position_; | 532 actual_menu_position_ = requested_menu_position_; |
| 640 canceled_ = false; | 533 canceled_ = false; |
| 641 | 534 |
| 642 has_mnemonics_ = has_mnemonics; | 535 has_mnemonics_ = has_mnemonics; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 return string16(); | 689 return string16(); |
| 797 } | 690 } |
| 798 | 691 |
| 799 Accelerator accelerator; | 692 Accelerator accelerator; |
| 800 return (GetDelegate() && | 693 return (GetDelegate() && |
| 801 GetDelegate()->GetAccelerator(GetCommand(), &accelerator)) ? | 694 GetDelegate()->GetAccelerator(GetCommand(), &accelerator)) ? |
| 802 accelerator.GetShortcutText() : string16(); | 695 accelerator.GetShortcutText() : string16(); |
| 803 } | 696 } |
| 804 | 697 |
| 805 } // namespace views | 698 } // namespace views |
| OLD | NEW |