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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/wrench_menu.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <set> 10 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/app/chrome_command_ids.h" 14 #include "chrome/browser/bookmarks/bookmark_model.h" 15 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 16 #include "chrome/browser/chrome_notification_types.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/search/search.h" 19 #include "chrome/browser/ui/browser.h" 20 #include "chrome/browser/ui/browser_window.h" 21 #include "chrome/browser/ui/tabs/tab_strip_model.h" 22 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h" 23 #include "content/public/browser/host_zoom_map.h" 24 #include "content/public/browser/notification_observer.h" 25 #include "content/public/browser/notification_registrar.h" 26 #include "content/public/browser/notification_source.h" 27 #include "content/public/browser/notification_types.h" 28 #include "content/public/browser/user_metrics.h" 29 #include "content/public/browser/web_contents.h" 30 #include "grit/chromium_strings.h" 31 #include "grit/generated_resources.h" 32 #include "grit/theme_resources.h" 33 #include "third_party/skia/include/core/SkCanvas.h" 34 #include "third_party/skia/include/core/SkPaint.h" 35 #include "ui/base/l10n/l10n_util.h" 36 #include "ui/base/layout.h" 37 #include "ui/base/resource/resource_bundle.h" 38 #include "ui/gfx/canvas.h" 39 #include "ui/gfx/image/canvas_image_source.h" 40 #include "ui/gfx/image/image.h" 41 #include "ui/gfx/skia_util.h" 42 #include "ui/gfx/text_utils.h" 43 #include "ui/views/background.h" 44 #include "ui/views/controls/button/image_button.h" 45 #include "ui/views/controls/button/label_button.h" 46 #include "ui/views/controls/button/menu_button.h" 47 #include "ui/views/controls/label.h" 48 #include "ui/views/controls/menu/menu_config.h" 49 #include "ui/views/controls/menu/menu_item_view.h" 50 #include "ui/views/controls/menu/menu_runner.h" 51 #include "ui/views/controls/menu/menu_scroll_view_container.h" 52 #include "ui/views/controls/menu/submenu_view.h" 53 #include "ui/views/widget/widget.h" 54 55 #if defined(USE_AURA) 56 #include "ui/native_theme/native_theme_aura.h" 57 #endif 58 59 using content::HostZoomMap; 60 using content::UserMetricsAction; 61 using content::WebContents; 62 using ui::MenuModel; 63 using views::CustomButton; 64 using views::ImageButton; 65 using views::Label; 66 using views::LabelButton; 67 using views::MenuConfig; 68 using views::MenuItemView; 69 using views::View; 70 71 namespace { 72 73 // Colors used for buttons. 74 const SkColor kEnabledTouchBackgroundColor = SkColorSetARGB(247, 255, 255, 255); 75 const SkColor kHoverTouchBackgroundColor = SkColorSetARGB(247, 242, 242, 242); 76 const SkColor kFocusedTouchBackgroundColor = SkColorSetARGB(247, 235, 235, 235); 77 78 const SkColor kTouchButtonText = 0xff5a5a5a; 79 80 // Horizontal padding on the edges of the buttons. 81 const int kHorizontalPadding = 6; 82 // Horizontal padding for a touch enabled menu. 83 const int kHorizontalTouchPadding = 15; 84 85 // Menu items which have embedded buttons should have this height in pixel. 86 const int kMenuItemContainingButtonsHeight = 43; 87 88 // Subclass of ImageButton whose preferred size includes the size of the border. 89 class FullscreenButton : public ImageButton { 90 public: 91 explicit FullscreenButton(views::ButtonListener* listener) 92 : ImageButton(listener) { } 93 94 // Overridden from ImageButton. 95 virtual gfx::Size GetPreferredSize() OVERRIDE { 96 gfx::Size pref = ImageButton::GetPreferredSize(); 97 if (border()) { 98 gfx::Insets insets = border()->GetInsets(); 99 pref.Enlarge(insets.width(), insets.height()); 100 } 101 return pref; 102 } 103 104 private: 105 DISALLOW_COPY_AND_ASSIGN(FullscreenButton); 106 }; 107 108 // Border for buttons contained in the menu. This is only used for getting the 109 // insets, the actual painting is done in MenuButtonBackground. 110 class MenuButtonBorder : public views::Border { 111 public: 112 MenuButtonBorder(const MenuConfig& config, bool use_new_menu) 113 : horizontal_padding_(use_new_menu ? 114 kHorizontalTouchPadding : kHorizontalPadding), 115 insets_(config.item_top_margin, horizontal_padding_, 116 config.item_bottom_margin, horizontal_padding_) { 117 } 118 119 // Overridden from views::Border. 120 virtual void Paint(const View& view, gfx::Canvas* canvas) OVERRIDE { 121 // Painting of border is done in MenuButtonBackground. 122 } 123 124 virtual gfx::Insets GetInsets() const OVERRIDE { 125 return insets_; 126 } 127 128 private: 129 // The horizontal padding dependent on the layout. 130 const int horizontal_padding_; 131 132 const gfx::Insets insets_; 133 134 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder); 135 }; 136 137 // Combination border/background for the buttons contained in the menu. The 138 // painting of the border/background is done here as TextButton does not always 139 // paint the border. 140 class MenuButtonBackground : public views::Background { 141 public: 142 enum ButtonType { 143 LEFT_BUTTON, 144 CENTER_BUTTON, 145 RIGHT_BUTTON, 146 SINGLE_BUTTON, 147 }; 148 149 MenuButtonBackground(ButtonType type, bool use_new_menu) 150 : type_(type), 151 use_new_menu_(use_new_menu), 152 left_button_(NULL), 153 right_button_(NULL) {} 154 155 // Used when the type is CENTER_BUTTON to determine if the left/right edge 156 // needs to be rendered selected. 157 void SetOtherButtons(CustomButton* left_button, CustomButton* right_button) { 158 if (base::i18n::IsRTL()) { 159 left_button_ = right_button; 160 right_button_ = left_button; 161 } else { 162 left_button_ = left_button; 163 right_button_ = right_button; 164 } 165 } 166 167 // Overridden from views::Background. 168 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { 169 CustomButton::ButtonState state = 170 (!strcmp(view->GetClassName(), views::Label::kViewClassName)) ? 171 CustomButton::STATE_NORMAL : static_cast<CustomButton*>(view)->state(); 172 int w = view->width(); 173 int h = view->height(); 174 #if defined(USE_AURA) 175 if (use_new_menu_ && 176 view->GetNativeTheme() == ui::NativeThemeAura::instance()) { 177 // Normal buttons get a border drawn on the right side and the rest gets 178 // filled in. The left button however does not get a line to combine 179 // buttons. 180 int border = 0; 181 if (type_ != RIGHT_BUTTON) { 182 border = 1; 183 canvas->FillRect(gfx::Rect(0, 0, border, h), 184 BorderColor(view, CustomButton::STATE_NORMAL)); 185 } 186 canvas->FillRect(gfx::Rect(border, 0, w - border, h), 187 touch_background_color(state)); 188 return; 189 } 190 #endif 191 const SkColor background = BackgroundColor(view, state); 192 const SkColor border = BorderColor(view, state); 193 switch (TypeAdjustedForRTL()) { 194 case LEFT_BUTTON: 195 canvas->FillRect(gfx::Rect(1, 1, w, h - 2), background); 196 canvas->FillRect(gfx::Rect(2, 0, w, 1), border); 197 canvas->FillRect(gfx::Rect(1, 1, 1, 1), border); 198 canvas->FillRect(gfx::Rect(0, 2, 1, h - 4), border); 199 canvas->FillRect(gfx::Rect(1, h - 2, 1, 1), border); 200 canvas->FillRect(gfx::Rect(2, h - 1, w, 1), border); 201 break; 202 203 case CENTER_BUTTON: { 204 canvas->FillRect(gfx::Rect(1, 1, w - 2, h - 2), background); 205 SkColor left_color = state != CustomButton::STATE_NORMAL ? 206 border : BorderColor(view, left_button_->state()); 207 canvas->FillRect(gfx::Rect(0, 0, 1, h), left_color); 208 canvas->FillRect(gfx::Rect(1, 0, w - 2, 1), border); 209 canvas->FillRect(gfx::Rect(1, h - 1, w - 2, 1), 210 border); 211 SkColor right_color = state != CustomButton::STATE_NORMAL ? 212 border : BorderColor(view, right_button_->state()); 213 canvas->FillRect(gfx::Rect(w - 1, 0, 1, h), right_color); 214 break; 215 } 216 217 case RIGHT_BUTTON: 218 canvas->FillRect(gfx::Rect(0, 1, w - 1, h - 2), background); 219 canvas->FillRect(gfx::Rect(0, 0, w - 2, 1), border); 220 canvas->FillRect(gfx::Rect(w - 2, 1, 1, 1), border); 221 canvas->FillRect(gfx::Rect(w - 1, 2, 1, h - 4), border); 222 canvas->FillRect(gfx::Rect(w - 2, h - 2, 1, 1), border); 223 canvas->FillRect(gfx::Rect(0, h - 1, w - 2, 1), border); 224 break; 225 226 case SINGLE_BUTTON: 227 canvas->FillRect(gfx::Rect(1, 1, w - 2, h - 2), background); 228 canvas->FillRect(gfx::Rect(2, 0, w - 4, 1), border); 229 canvas->FillRect(gfx::Rect(1, 1, 1, 1), border); 230 canvas->FillRect(gfx::Rect(0, 2, 1, h - 4), border); 231 canvas->FillRect(gfx::Rect(1, h - 2, 1, 1), border); 232 canvas->FillRect(gfx::Rect(2, h - 1, w - 4, 1), border); 233 canvas->FillRect(gfx::Rect(w - 2, 1, 1, 1), border); 234 canvas->FillRect(gfx::Rect(w - 1, 2, 1, h - 4), border); 235 canvas->FillRect(gfx::Rect(w - 2, h - 2, 1, 1), border); 236 break; 237 238 default: 239 NOTREACHED(); 240 break; 241 } 242 } 243 244 private: 245 static SkColor BorderColor(View* view, CustomButton::ButtonState state) { 246 ui::NativeTheme* theme = view->GetNativeTheme(); 247 switch (state) { 248 case CustomButton::STATE_HOVERED: 249 return theme->GetSystemColor( 250 ui::NativeTheme::kColorId_HoverMenuButtonBorderColor); 251 case CustomButton::STATE_PRESSED: 252 return theme->GetSystemColor( 253 ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor); 254 default: 255 return theme->GetSystemColor( 256 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor); 257 } 258 } 259 260 static SkColor BackgroundColor(View* view, CustomButton::ButtonState state) { 261 ui::NativeTheme* theme = view->GetNativeTheme(); 262 switch (state) { 263 case CustomButton::STATE_HOVERED: 264 return theme->GetSystemColor( 265 ui::NativeTheme::kColorId_HoverMenuItemBackgroundColor); 266 case CustomButton::STATE_PRESSED: 267 return theme->GetSystemColor( 268 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor); 269 default: 270 return theme->GetSystemColor( 271 ui::NativeTheme::kColorId_MenuBackgroundColor); 272 } 273 } 274 275 static SkColor touch_background_color(CustomButton::ButtonState state) { 276 switch (state) { 277 case CustomButton::STATE_HOVERED: return kHoverTouchBackgroundColor; 278 case CustomButton::STATE_PRESSED: return kFocusedTouchBackgroundColor; 279 default: return kEnabledTouchBackgroundColor; 280 } 281 } 282 283 ButtonType TypeAdjustedForRTL() const { 284 if (!base::i18n::IsRTL()) 285 return type_; 286 287 switch (type_) { 288 case LEFT_BUTTON: return RIGHT_BUTTON; 289 case RIGHT_BUTTON: return LEFT_BUTTON; 290 default: break; 291 } 292 return type_; 293 } 294 295 const ButtonType type_; 296 const bool use_new_menu_; 297 298 // See description above setter for details. 299 CustomButton* left_button_; 300 CustomButton* right_button_; 301 302 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground); 303 }; 304 305 string16 GetAccessibleNameForWrenchMenuItem( 306 MenuModel* model, int item_index, int accessible_string_id) { 307 string16 accessible_name = l10n_util::GetStringUTF16(accessible_string_id); 308 string16 accelerator_text; 309 310 ui::Accelerator menu_accelerator; 311 if (model->GetAcceleratorAt(item_index, &menu_accelerator)) { 312 accelerator_text = 313 ui::Accelerator(menu_accelerator.key_code(), 314 menu_accelerator.modifiers()).GetShortcutText(); 315 } 316 317 return MenuItemView::GetAccessibleNameForMenuItem( 318 accessible_name, accelerator_text); 319 } 320 321 // WrenchMenuView is a view that can contain label buttons. 322 class WrenchMenuView : public views::View, 323 public views::ButtonListener { 324 public: 325 WrenchMenuView(WrenchMenu* menu, MenuModel* menu_model) 326 : menu_(menu), 327 menu_model_(menu_model) {} 328 329 // Overridden from views::View. 330 virtual void SchedulePaintInRect(const gfx::Rect& r) OVERRIDE { 331 // Normally when the mouse enters/exits a button the buttons invokes 332 // SchedulePaint. As part of the button border (MenuButtonBackground) is 333 // rendered by the button to the left/right of it SchedulePaint on the the 334 // button may not be enough, so this forces a paint all. 335 View::SchedulePaintInRect(gfx::Rect(size())); 336 } 337 338 LabelButton* CreateAndConfigureButton(int string_id, 339 MenuButtonBackground::ButtonType type, 340 int index, 341 MenuButtonBackground** background) { 342 return CreateButtonWithAccName( 343 string_id, type, index, background, string_id); 344 } 345 346 LabelButton* CreateButtonWithAccName(int string_id, 347 MenuButtonBackground::ButtonType type, 348 int index, 349 MenuButtonBackground** background, 350 int acc_string_id) { 351 LabelButton* button = new LabelButton(this, gfx::RemoveAcceleratorChar( 352 l10n_util::GetStringUTF16(string_id), '&', NULL, NULL)); 353 button->SetAccessibleName( 354 GetAccessibleNameForWrenchMenuItem(menu_model_, index, acc_string_id)); 355 button->set_focusable(true); 356 button->set_request_focus_on_press(false); 357 button->set_tag(index); 358 button->SetEnabled(menu_model_->IsEnabledAt(index)); 359 MenuButtonBackground* bg = 360 new MenuButtonBackground(type, menu_->use_new_menu()); 361 button->set_background(bg); 362 const MenuConfig& menu_config = menu_->GetMenuConfig(); 363 button->SetTextColor(views::Button::STATE_NORMAL, menu_config.text_color); 364 if (background) 365 *background = bg; 366 button->set_border( 367 new MenuButtonBorder(menu_config, menu_->use_new_menu())); 368 button->SetHorizontalAlignment(gfx::ALIGN_CENTER); 369 button->SetFont(menu_config.font); 370 AddChildView(button); 371 return button; 372 } 373 374 protected: 375 // Hosting WrenchMenu. 376 WrenchMenu* menu_; 377 378 // The menu model containing the increment/decrement/reset items. 379 MenuModel* menu_model_; 380 381 private: 382 DISALLOW_COPY_AND_ASSIGN(WrenchMenuView); 383 }; 384 385 class ButtonContainerMenuItemView : public MenuItemView { 386 public: 387 // Constructor for use with button containing menu items which have a 388 // different height then normal items. 389 ButtonContainerMenuItemView(MenuItemView* parent, int id, int height) 390 : MenuItemView(parent, id, MenuItemView::NORMAL), 391 height_(height) { 392 }; 393 394 // Overridden from MenuItemView. 395 virtual gfx::Size GetChildPreferredSize() OVERRIDE { 396 gfx::Size size = MenuItemView::GetChildPreferredSize(); 397 // When there is a height override given, we need to deduct our spacing 398 // above and below to get to the correct height to return here for the 399 // child item. 400 int height = height_ - GetTopMargin() - GetBottomMargin(); 401 if (height > size.height()) 402 size.set_height(height); 403 return size; 404 } 405 406 private: 407 int height_; 408 409 DISALLOW_COPY_AND_ASSIGN(ButtonContainerMenuItemView); 410 }; 411 412 } // namespace 413 414 // CutCopyPasteView ------------------------------------------------------------ 415 416 // CutCopyPasteView is the view containing the cut/copy/paste buttons. 417 class WrenchMenu::CutCopyPasteView : public WrenchMenuView { 418 public: 419 CutCopyPasteView(WrenchMenu* menu, 420 MenuModel* menu_model, 421 const ui::NativeTheme* native_theme, 422 int cut_index, 423 int copy_index, 424 int paste_index) 425 : WrenchMenuView(menu, menu_model) { 426 LabelButton* cut = CreateAndConfigureButton( 427 IDS_CUT, MenuButtonBackground::LEFT_BUTTON, cut_index, NULL); 428 429 MenuButtonBackground* copy_background = NULL; 430 LabelButton* copy = CreateAndConfigureButton( 431 IDS_COPY, MenuButtonBackground::CENTER_BUTTON, copy_index, 432 ©_background); 433 434 LabelButton* paste = CreateAndConfigureButton( 435 IDS_PASTE, 436 menu_->use_new_menu() && menu_->supports_new_separators_ ? 437 MenuButtonBackground::CENTER_BUTTON : 438 MenuButtonBackground::RIGHT_BUTTON, 439 paste_index, 440 NULL); 441 if (menu_->use_new_menu()) { 442 cut->SetTextColor(views::Button::STATE_NORMAL, kTouchButtonText); 443 copy->SetTextColor(views::Button::STATE_NORMAL, kTouchButtonText); 444 paste->SetTextColor(views::Button::STATE_NORMAL, kTouchButtonText); 445 } else { 446 SkColor text_color = native_theme->GetSystemColor( 447 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor); 448 cut->SetTextColor(views::Button::STATE_NORMAL, text_color); 449 copy->SetTextColor(views::Button::STATE_NORMAL, text_color); 450 paste->SetTextColor(views::Button::STATE_NORMAL, text_color); 451 } 452 copy_background->SetOtherButtons(cut, paste); 453 } 454 455 // Overridden from View. 456 virtual gfx::Size GetPreferredSize() OVERRIDE { 457 // Returned height doesn't matter as MenuItemView forces everything to the 458 // height of the menuitemview. 459 return gfx::Size(GetMaxChildViewPreferredWidth() * child_count(), 0); 460 } 461 462 virtual void Layout() OVERRIDE { 463 // All buttons are given the same width. 464 int width = GetMaxChildViewPreferredWidth(); 465 for (int i = 0; i < child_count(); ++i) 466 child_at(i)->SetBounds(i * width, 0, width, height()); 467 } 468 469 // Overridden from ButtonListener. 470 virtual void ButtonPressed(views::Button* sender, 471 const ui::Event& event) OVERRIDE { 472 menu_->CancelAndEvaluate(menu_model_, sender->tag()); 473 } 474 475 private: 476 // Returns the max preferred width of all the children. 477 int GetMaxChildViewPreferredWidth() { 478 int width = 0; 479 for (int i = 0; i < child_count(); ++i) 480 width = std::max(width, child_at(i)->GetPreferredSize().width()); 481 return width; 482 } 483 484 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView); 485 }; 486 487 // ZoomView -------------------------------------------------------------------- 488 489 // Padding between the increment buttons and the reset button. 490 static const int kZoomPadding = 6; 491 static const int kTouchZoomPadding = 14; 492 493 // ZoomView contains the various zoom controls: two buttons to increase/decrease 494 // the zoom, a label showing the current zoom percent, and a button to go 495 // full-screen. 496 class WrenchMenu::ZoomView : public WrenchMenuView { 497 public: 498 ZoomView(WrenchMenu* menu, 499 MenuModel* menu_model, 500 const ui::NativeTheme* native_theme, 501 int decrement_index, 502 int increment_index, 503 int fullscreen_index) 504 : WrenchMenuView(menu, menu_model), 505 fullscreen_index_(fullscreen_index), 506 zoom_callback_(base::Bind(&WrenchMenu::ZoomView::OnZoomLevelChanged, 507 base::Unretained(this))), 508 increment_button_(NULL), 509 zoom_label_(NULL), 510 decrement_button_(NULL), 511 fullscreen_button_(NULL), 512 zoom_label_width_(0) { 513 HostZoomMap::GetForBrowserContext( 514 menu_->browser_->profile())->AddZoomLevelChangedCallback( 515 zoom_callback_); 516 517 decrement_button_ = CreateButtonWithAccName( 518 IDS_ZOOM_MINUS2, MenuButtonBackground::LEFT_BUTTON, decrement_index, 519 NULL, IDS_ACCNAME_ZOOM_MINUS2); 520 521 zoom_label_ = new Label( 522 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100)); 523 zoom_label_->SetAutoColorReadabilityEnabled(false); 524 zoom_label_->SetHorizontalAlignment(gfx::ALIGN_RIGHT); 525 526 MenuButtonBackground* center_bg = new MenuButtonBackground( 527 menu_->use_new_menu() && menu_->supports_new_separators_ ? 528 MenuButtonBackground::RIGHT_BUTTON : 529 MenuButtonBackground::CENTER_BUTTON, 530 menu_->use_new_menu()); 531 zoom_label_->set_background(center_bg); 532 const MenuConfig& menu_config(menu->GetMenuConfig()); 533 zoom_label_->set_border( 534 new MenuButtonBorder(menu_config, menu->use_new_menu())); 535 zoom_label_->SetFont(menu_config.font); 536 537 AddChildView(zoom_label_); 538 zoom_label_width_ = MaxWidthForZoomLabel(); 539 540 increment_button_ = CreateButtonWithAccName( 541 IDS_ZOOM_PLUS2, MenuButtonBackground::RIGHT_BUTTON, increment_index, 542 NULL, IDS_ACCNAME_ZOOM_PLUS2); 543 544 center_bg->SetOtherButtons(decrement_button_, increment_button_); 545 546 fullscreen_button_ = new FullscreenButton(this); 547 gfx::ImageSkia* full_screen_image = 548 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 549 IDR_FULLSCREEN_MENU_BUTTON); 550 fullscreen_button_->SetImage(ImageButton::STATE_NORMAL, full_screen_image); 551 if (menu_->use_new_menu()) { 552 zoom_label_->SetEnabledColor(kTouchButtonText); 553 decrement_button_->SetTextColor(views::Button::STATE_NORMAL, 554 kTouchButtonText); 555 increment_button_->SetTextColor(views::Button::STATE_NORMAL, 556 kTouchButtonText); 557 } else { 558 SkColor enabled_text_color = native_theme->GetSystemColor( 559 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor); 560 zoom_label_->SetEnabledColor(enabled_text_color); 561 decrement_button_->SetTextColor(views::Button::STATE_NORMAL, 562 enabled_text_color); 563 increment_button_->SetTextColor(views::Button::STATE_NORMAL, 564 enabled_text_color); 565 SkColor disabled_text_color = native_theme->GetSystemColor( 566 ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor); 567 decrement_button_->SetTextColor(views::Button::STATE_DISABLED, 568 disabled_text_color); 569 increment_button_->SetTextColor(views::Button::STATE_DISABLED, 570 disabled_text_color); 571 } 572 573 fullscreen_button_->set_focusable(true); 574 fullscreen_button_->set_request_focus_on_press(false); 575 fullscreen_button_->set_tag(fullscreen_index); 576 fullscreen_button_->SetImageAlignment( 577 ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE); 578 int horizontal_padding = 579 menu_->use_new_menu() ? kHorizontalTouchPadding : kHorizontalPadding; 580 fullscreen_button_->set_border(views::Border::CreateEmptyBorder( 581 0, horizontal_padding, 0, horizontal_padding)); 582 fullscreen_button_->set_background( 583 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON, 584 menu_->use_new_menu())); 585 fullscreen_button_->SetAccessibleName( 586 GetAccessibleNameForWrenchMenuItem( 587 menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN)); 588 AddChildView(fullscreen_button_); 589 590 UpdateZoomControls(); 591 } 592 593 virtual ~ZoomView() { 594 HostZoomMap::GetForBrowserContext( 595 menu_->browser_->profile())->RemoveZoomLevelChangedCallback( 596 zoom_callback_); 597 } 598 599 // Overridden from View. 600 virtual gfx::Size GetPreferredSize() OVERRIDE { 601 // The increment/decrement button are forced to the same width. 602 int button_width = std::max(increment_button_->GetPreferredSize().width(), 603 decrement_button_->GetPreferredSize().width()); 604 int zoom_padding = menu_->use_new_menu() ? kTouchZoomPadding : kZoomPadding; 605 int fullscreen_width = fullscreen_button_->GetPreferredSize().width() + 606 zoom_padding; 607 // Returned height doesn't matter as MenuItemView forces everything to the 608 // height of the menuitemview. Note that we have overridden the height when 609 // constructing the menu. 610 return gfx::Size(button_width + zoom_label_width_ + button_width + 611 fullscreen_width, 0); 612 } 613 614 virtual void Layout() OVERRIDE { 615 int x = 0; 616 int button_width = std::max(increment_button_->GetPreferredSize().width(), 617 decrement_button_->GetPreferredSize().width()); 618 gfx::Rect bounds(0, 0, button_width, height()); 619 620 decrement_button_->SetBoundsRect(bounds); 621 622 x += bounds.width(); 623 bounds.set_x(x); 624 bounds.set_width(zoom_label_width_); 625 zoom_label_->SetBoundsRect(bounds); 626 627 x += bounds.width(); 628 bounds.set_x(x); 629 bounds.set_width(button_width); 630 increment_button_->SetBoundsRect(bounds); 631 632 x += bounds.width() + (menu_->use_new_menu() ? 0 : kZoomPadding); 633 bounds.set_x(x); 634 bounds.set_width(fullscreen_button_->GetPreferredSize().width() + 635 (menu_->use_new_menu() ? kTouchZoomPadding : 0)); 636 fullscreen_button_->SetBoundsRect(bounds); 637 } 638 639 // Overridden from ButtonListener. 640 virtual void ButtonPressed(views::Button* sender, 641 const ui::Event& event) OVERRIDE { 642 if (sender->tag() == fullscreen_index_) { 643 menu_->CancelAndEvaluate(menu_model_, sender->tag()); 644 } else { 645 // Zoom buttons don't close the menu. 646 menu_model_->ActivatedAt(sender->tag()); 647 } 648 } 649 650 private: 651 void OnZoomLevelChanged(const HostZoomMap::ZoomLevelChange& change) { 652 UpdateZoomControls(); 653 } 654 655 void UpdateZoomControls() { 656 bool enable_increment = false; 657 bool enable_decrement = false; 658 WebContents* selected_tab = 659 menu_->browser_->tab_strip_model()->GetActiveWebContents(); 660 int zoom = 100; 661 if (selected_tab) 662 zoom = selected_tab->GetZoomPercent(&enable_increment, &enable_decrement); 663 increment_button_->SetEnabled(enable_increment); 664 decrement_button_->SetEnabled(enable_decrement); 665 zoom_label_->SetText( 666 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, zoom)); 667 668 zoom_label_width_ = MaxWidthForZoomLabel(); 669 } 670 671 // Calculates the max width the zoom string can be. 672 int MaxWidthForZoomLabel() { 673 gfx::Font font = zoom_label_->font(); 674 int border_width = 675 zoom_label_->border() ? zoom_label_->border()->GetInsets().width() : 0; 676 677 int max_w = 0; 678 679 WebContents* selected_tab = 680 menu_->browser_->tab_strip_model()->GetActiveWebContents(); 681 if (selected_tab) { 682 int min_percent = selected_tab->GetMinimumZoomPercent(); 683 int max_percent = selected_tab->GetMaximumZoomPercent(); 684 685 int step = (max_percent - min_percent) / 10; 686 for (int i = min_percent; i <= max_percent; i += step) { 687 int w = font.GetStringWidth( 688 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, i)); 689 max_w = std::max(w, max_w); 690 } 691 } else { 692 max_w = font.GetStringWidth( 693 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100)); 694 } 695 696 return max_w + border_width; 697 } 698 699 // Index of the fullscreen menu item in the model. 700 const int fullscreen_index_; 701 702 content::HostZoomMap::ZoomLevelChangedCallback zoom_callback_; 703 content::NotificationRegistrar registrar_; 704 705 // Button for incrementing the zoom. 706 LabelButton* increment_button_; 707 708 // Label showing zoom as a percent. 709 Label* zoom_label_; 710 711 // Button for decrementing the zoom. 712 LabelButton* decrement_button_; 713 714 ImageButton* fullscreen_button_; 715 716 // Width given to |zoom_label_|. This is the width at 100%. 717 int zoom_label_width_; 718 719 DISALLOW_COPY_AND_ASSIGN(ZoomView); 720 }; 721 722 // RecentTabsMenuModelDelegate ------------------------------------------------- 723 724 // Provides the ui::MenuModelDelegate implementation for RecentTabsSubMenuModel 725 // items. 726 class WrenchMenu::RecentTabsMenuModelDelegate : public ui::MenuModelDelegate { 727 public: 728 RecentTabsMenuModelDelegate(ui::MenuModel* model, 729 views::MenuItemView* menu_item) 730 : model_(model), 731 menu_item_(menu_item) { 732 model_->SetMenuModelDelegate(this); 733 } 734 735 virtual ~RecentTabsMenuModelDelegate() { 736 model_->SetMenuModelDelegate(NULL); 737 } 738 739 // ui::MenuModelDelegate implementation: 740 virtual void OnIconChanged(int index) OVERRIDE { 741 // |index| specifies position in children items of |menu_item_| starting at 742 // 0, its corresponding command id as used in the children menu item views 743 // follows that of the parent menu item view |menu_item_|. 744 int command_id = menu_item_->GetCommand() + 1 + index; 745 views::MenuItemView* item = menu_item_->GetMenuItemByID(command_id); 746 DCHECK(item); 747 gfx::Image icon; 748 if (model_->GetIconAt(index, &icon)) 749 item->SetIcon(*icon.ToImageSkia()); 750 } 751 752 // Return the specific menu width of recent tab menu item if |command_id| 753 // refers to one of recent tabs menu items, else return -1. 754 int GetMaxWidthForMenu(MenuItemView* menu) { 755 views::SubmenuView* submenu = menu_item_->GetSubmenu(); 756 if (!submenu) 757 return -1; 758 const int kMaxMenuItemWidth = 320; 759 return menu->GetCommand() >= menu_item_->GetCommand() && 760 menu->GetCommand() <= 761 menu_item_->GetCommand() + submenu->GetMenuItemCount() ? 762 kMaxMenuItemWidth : -1; 763 } 764 765 const gfx::Font* GetLabelFontAt(int index) const { 766 return model_->GetLabelFontAt(index); 767 } 768 769 bool GetForegroundColor(int command_id, 770 bool is_hovered, 771 SkColor* override_color) const { 772 // The items for which we get a font, should be shown in black. 773 if (GetLabelFontAt(command_id)) { 774 *override_color = SK_ColorBLACK; 775 return true; 776 } 777 return false; 778 } 779 780 private: 781 ui::MenuModel* model_; 782 views::MenuItemView* menu_item_; 783 784 DISALLOW_COPY_AND_ASSIGN(RecentTabsMenuModelDelegate); 785 }; 786 787 // WrenchMenu ------------------------------------------------------------------ 788 789 WrenchMenu::WrenchMenu(Browser* browser, 790 bool use_new_menu, 791 bool supports_new_separators) 792 : root_(NULL), 793 browser_(browser), 794 selected_menu_model_(NULL), 795 selected_index_(0), 796 bookmark_menu_(NULL), 797 feedback_menu_item_(NULL), 798 first_bookmark_command_id_(0), 799 first_recent_tabs_command_id_(-1), 800 last_recent_tabs_command_id_(-1), 801 use_new_menu_(use_new_menu), 802 supports_new_separators_(supports_new_separators) { 803 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED, 804 content::Source<Profile>(browser_->profile())); 805 } 806 807 WrenchMenu::~WrenchMenu() { 808 if (bookmark_menu_delegate_.get()) { 809 BookmarkModel* model = BookmarkModelFactory::GetForProfile( 810 browser_->profile()); 811 if (model) 812 model->RemoveObserver(this); 813 } 814 } 815 816 void WrenchMenu::Init(ui::MenuModel* model) { 817 DCHECK(!root_); 818 root_ = new MenuItemView(this); 819 root_->set_has_icons(true); // We have checks, radios and icons, set this 820 // so we get the taller menu style. 821 int next_id = 1; 822 PopulateMenu(root_, model, &next_id); 823 first_bookmark_command_id_ = next_id + 1; 824 menu_runner_.reset(new views::MenuRunner(root_)); 825 } 826 827 void WrenchMenu::RunMenu(views::MenuButton* host) { 828 gfx::Point screen_loc; 829 views::View::ConvertPointToScreen(host, &screen_loc); 830 gfx::Rect bounds(screen_loc, host->size()); 831 content::RecordAction(UserMetricsAction("ShowAppMenu")); 832 if (menu_runner_->RunMenuAt(host->GetWidget(), host, bounds, 833 MenuItemView::TOPRIGHT, ui::MENU_SOURCE_NONE, 834 views::MenuRunner::HAS_MNEMONICS) == 835 views::MenuRunner::MENU_DELETED) 836 return; 837 if (bookmark_menu_delegate_.get()) { 838 BookmarkModel* model = BookmarkModelFactory::GetForProfile( 839 browser_->profile()); 840 if (model) 841 model->RemoveObserver(this); 842 } 843 if (selected_menu_model_) 844 selected_menu_model_->ActivatedAt(selected_index_); 845 } 846 847 bool WrenchMenu::IsShowing() { 848 return menu_runner_.get() && menu_runner_->IsRunning(); 849 } 850 851 const ui::NativeTheme* WrenchMenu::GetNativeTheme() const { 852 views::Widget* browser_widget = views::Widget::GetWidgetForNativeView( 853 browser_->window()->GetNativeWindow()); 854 DCHECK(browser_widget); 855 return browser_widget->GetNativeTheme(); 856 } 857 858 const views::MenuConfig& WrenchMenu::GetMenuConfig() const { 859 return MenuConfig::instance(GetNativeTheme()); 860 } 861 862 const gfx::Font* WrenchMenu::GetLabelFont(int index) const { 863 if (is_recent_tabs_command(index)) { 864 return recent_tabs_menu_model_delegate_->GetLabelFontAt( 865 index - first_recent_tabs_command_id_); 866 } 867 return NULL; 868 } 869 870 bool WrenchMenu::GetForegroundColor(int command_id, 871 bool is_hovered, 872 SkColor* override_color) const { 873 if (is_recent_tabs_command(command_id)) { 874 return recent_tabs_menu_model_delegate_->GetForegroundColor( 875 command_id - first_recent_tabs_command_id_, 876 is_hovered, 877 override_color); 878 } 879 return false; 880 } 881 882 string16 WrenchMenu::GetTooltipText(int id, 883 const gfx::Point& p) const { 884 return is_bookmark_command(id) ? 885 bookmark_menu_delegate_->GetTooltipText(id, p) : string16(); 886 } 887 888 bool WrenchMenu::IsTriggerableEvent(views::MenuItemView* menu, 889 const ui::Event& e) { 890 return is_bookmark_command(menu->GetCommand()) ? 891 bookmark_menu_delegate_->IsTriggerableEvent(menu, e) : 892 MenuDelegate::IsTriggerableEvent(menu, e); 893 } 894 895 bool WrenchMenu::GetDropFormats( 896 MenuItemView* menu, 897 int* formats, 898 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) { 899 CreateBookmarkMenu(); 900 return bookmark_menu_delegate_.get() && 901 bookmark_menu_delegate_->GetDropFormats(menu, formats, custom_formats); 902 } 903 904 bool WrenchMenu::AreDropTypesRequired(MenuItemView* menu) { 905 CreateBookmarkMenu(); 906 return bookmark_menu_delegate_.get() && 907 bookmark_menu_delegate_->AreDropTypesRequired(menu); 908 } 909 910 bool WrenchMenu::CanDrop(MenuItemView* menu, 911 const ui::OSExchangeData& data) { 912 CreateBookmarkMenu(); 913 return bookmark_menu_delegate_.get() && 914 bookmark_menu_delegate_->CanDrop(menu, data); 915 } 916 917 int WrenchMenu::GetDropOperation( 918 MenuItemView* item, 919 const ui::DropTargetEvent& event, 920 DropPosition* position) { 921 return is_bookmark_command(item->GetCommand()) ? 922 bookmark_menu_delegate_->GetDropOperation(item, event, position) : 923 ui::DragDropTypes::DRAG_NONE; 924 } 925 926 int WrenchMenu::OnPerformDrop(MenuItemView* menu, 927 DropPosition position, 928 const ui::DropTargetEvent& event) { 929 if (!is_bookmark_command(menu->GetCommand())) 930 return ui::DragDropTypes::DRAG_NONE; 931 932 int result = bookmark_menu_delegate_->OnPerformDrop(menu, position, event); 933 return result; 934 } 935 936 bool WrenchMenu::ShowContextMenu(MenuItemView* source, 937 int id, 938 const gfx::Point& p, 939 ui::MenuSourceType source_type) { 940 return is_bookmark_command(id) ? 941 bookmark_menu_delegate_->ShowContextMenu(source, id, p, 942 source_type) : 943 false; 944 } 945 946 bool WrenchMenu::CanDrag(MenuItemView* menu) { 947 return is_bookmark_command(menu->GetCommand()) ? 948 bookmark_menu_delegate_->CanDrag(menu) : false; 949 } 950 951 void WrenchMenu::WriteDragData(MenuItemView* sender, 952 ui::OSExchangeData* data) { 953 DCHECK(is_bookmark_command(sender->GetCommand())); 954 return bookmark_menu_delegate_->WriteDragData(sender, data); 955 } 956 957 int WrenchMenu::GetDragOperations(MenuItemView* sender) { 958 return is_bookmark_command(sender->GetCommand()) ? 959 bookmark_menu_delegate_->GetDragOperations(sender) : 960 MenuDelegate::GetDragOperations(sender); 961 } 962 963 int WrenchMenu::GetMaxWidthForMenu(MenuItemView* menu) { 964 if (is_bookmark_command(menu->GetCommand())) 965 return bookmark_menu_delegate_->GetMaxWidthForMenu(menu); 966 int max_width = -1; 967 // If recent tabs menu is available, it will decide if |menu| is one of recent 968 // tabs; if yes, it would return the menu width for recent tabs. 969 // otherwise, it would return -1. 970 if (recent_tabs_menu_model_delegate_.get()) 971 max_width = recent_tabs_menu_model_delegate_->GetMaxWidthForMenu(menu); 972 if (max_width == -1) 973 max_width = MenuDelegate::GetMaxWidthForMenu(menu); 974 return max_width; 975 } 976 977 bool WrenchMenu::IsItemChecked(int id) const { 978 if (is_bookmark_command(id)) 979 return false; 980 981 const Entry& entry = id_to_entry_.find(id)->second; 982 return entry.first->IsItemCheckedAt(entry.second); 983 } 984 985 bool WrenchMenu::IsCommandEnabled(int id) const { 986 if (is_bookmark_command(id)) 987 return true; 988 989 if (id == 0) 990 return false; // The root item. 991 992 const Entry& entry = id_to_entry_.find(id)->second; 993 int command_id = entry.first->GetCommandIdAt(entry.second); 994 // The items representing the cut menu (cut/copy/paste) and zoom menu 995 // (increment/decrement/reset) are always enabled. The child views of these 996 // items enabled state updates appropriately. 997 return command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS || 998 entry.first->IsEnabledAt(entry.second); 999 } 1000 1001 void WrenchMenu::ExecuteCommand(int id, int mouse_event_flags) { 1002 if (is_bookmark_command(id)) { 1003 bookmark_menu_delegate_->ExecuteCommand(id, mouse_event_flags); 1004 return; 1005 } 1006 1007 // Not a bookmark 1008 const Entry& entry = id_to_entry_.find(id)->second; 1009 int command_id = entry.first->GetCommandIdAt(entry.second); 1010 1011 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { 1012 // These items are represented by child views. If ExecuteCommand is invoked 1013 // it means the user clicked on the area around the buttons and we should 1014 // not do anyting. 1015 return; 1016 } 1017 1018 return entry.first->ActivatedAt(entry.second, mouse_event_flags); 1019 } 1020 1021 bool WrenchMenu::GetAccelerator(int id, ui::Accelerator* accelerator) { 1022 if (is_bookmark_command(id)) 1023 return false; 1024 IDToEntry::iterator ix = id_to_entry_.find(id); 1025 if (ix == id_to_entry_.end()) { 1026 // There is no entry for this id. 1027 return false; 1028 } 1029 1030 const Entry& entry = ix->second; 1031 int command_id = entry.first->GetCommandIdAt(entry.second); 1032 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { 1033 // These have special child views; don't show the accelerator for them. 1034 return false; 1035 } 1036 1037 ui::Accelerator menu_accelerator; 1038 if (!entry.first->GetAcceleratorAt(entry.second, &menu_accelerator)) 1039 return false; 1040 1041 *accelerator = ui::Accelerator(menu_accelerator.key_code(), 1042 menu_accelerator.modifiers()); 1043 return true; 1044 } 1045 1046 void WrenchMenu::WillShowMenu(MenuItemView* menu) { 1047 if (menu == bookmark_menu_) 1048 CreateBookmarkMenu(); 1049 } 1050 1051 void WrenchMenu::WillHideMenu(MenuItemView* menu) { 1052 // Turns off the fade out animation of the wrench menus if 1053 // |feedback_menu_item_| is selected. This excludes the wrench menu itself 1054 // from the snapshot in the feedback UI. 1055 if (menu->HasSubmenu() && feedback_menu_item_ && 1056 feedback_menu_item_->IsSelected()) { 1057 // It's okay to just turn off the animation and no to take care the 1058 // animation back because the menu widget will be recreated next time 1059 // it's opened. See ToolbarView::RunMenu() and Init() of this class. 1060 menu->GetSubmenu()->GetWidget()-> 1061 SetVisibilityChangedAnimationsEnabled(false); 1062 } 1063 } 1064 1065 void WrenchMenu::BookmarkModelChanged() { 1066 DCHECK(bookmark_menu_delegate_.get()); 1067 if (!bookmark_menu_delegate_->is_mutating_model()) 1068 root_->Cancel(); 1069 } 1070 1071 void WrenchMenu::Observe(int type, 1072 const content::NotificationSource& source, 1073 const content::NotificationDetails& details) { 1074 switch (type) { 1075 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED: 1076 // A change in the global errors list can add or remove items from the 1077 // menu. Close the menu to avoid have a stale menu on-screen. 1078 root_->Cancel(); 1079 break; 1080 default: 1081 NOTREACHED(); 1082 } 1083 } 1084 1085 void WrenchMenu::PopulateMenu(MenuItemView* parent, 1086 MenuModel* model, 1087 int* next_id) { 1088 for (int i = 0, max = model->GetItemCount(); i < max; ++i) { 1089 // The button container menu items have a special height which we have to 1090 // use instead of the normal height. 1091 int height = 0; 1092 if (use_new_menu_ && 1093 (model->GetCommandIdAt(i) == IDC_CUT || 1094 model->GetCommandIdAt(i) == IDC_ZOOM_MINUS)) 1095 height = kMenuItemContainingButtonsHeight; 1096 1097 MenuItemView* item = AppendMenuItem( 1098 parent, model, i, model->GetTypeAt(i), next_id, height); 1099 1100 if (model->GetTypeAt(i) == MenuModel::TYPE_SUBMENU) { 1101 bool is_recent_tabs_menu = 1102 model->GetCommandIdAt(i) == IDC_RECENT_TABS_MENU; 1103 if (is_recent_tabs_menu) 1104 first_recent_tabs_command_id_ = *next_id; 1105 PopulateMenu(item, model->GetSubmenuModelAt(i), next_id); 1106 if (is_recent_tabs_menu) 1107 last_recent_tabs_command_id_ = *next_id - 1; 1108 } 1109 1110 const ui::NativeTheme* native_theme = GetNativeTheme(); 1111 1112 switch (model->GetCommandIdAt(i)) { 1113 case IDC_CUT: 1114 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(i)); 1115 DCHECK_LT(i + 2, max); 1116 DCHECK_EQ(IDC_COPY, model->GetCommandIdAt(i + 1)); 1117 DCHECK_EQ(IDC_PASTE, model->GetCommandIdAt(i + 2)); 1118 item->SetTitle(l10n_util::GetStringUTF16(IDS_EDIT2)); 1119 item->AddChildView(new CutCopyPasteView(this, model, native_theme, 1120 i, i + 1, i + 2)); 1121 i += 2; 1122 break; 1123 1124 case IDC_ZOOM_MINUS: 1125 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(i)); 1126 DCHECK_EQ(IDC_ZOOM_PLUS, model->GetCommandIdAt(i + 1)); 1127 DCHECK_EQ(IDC_FULLSCREEN, model->GetCommandIdAt(i + 2)); 1128 item->SetTitle(l10n_util::GetStringUTF16(IDS_ZOOM_MENU2)); 1129 item->AddChildView(new ZoomView(this, model, native_theme, 1130 i, i + 1, i + 2)); 1131 i += 2; 1132 break; 1133 1134 case IDC_BOOKMARKS_MENU: 1135 DCHECK(!bookmark_menu_); 1136 bookmark_menu_ = item; 1137 break; 1138 1139 case IDC_FEEDBACK: 1140 DCHECK(!feedback_menu_item_); 1141 feedback_menu_item_ = item; 1142 break; 1143 1144 case IDC_RECENT_TABS_MENU: 1145 DCHECK(!recent_tabs_menu_model_delegate_.get()); 1146 recent_tabs_menu_model_delegate_.reset( 1147 new RecentTabsMenuModelDelegate(model->GetSubmenuModelAt(i), 1148 item)); 1149 break; 1150 1151 default: 1152 break; 1153 } 1154 } 1155 } 1156 1157 MenuItemView* WrenchMenu::AppendMenuItem(MenuItemView* parent, 1158 MenuModel* model, 1159 int index, 1160 MenuModel::ItemType menu_type, 1161 int* next_id, 1162 int height) { 1163 int id = (*next_id)++; 1164 1165 id_to_entry_[id].first = model; 1166 id_to_entry_[id].second = index; 1167 1168 MenuItemView* menu_item = NULL; 1169 if (height > 0) { 1170 // For menu items with a special menu height we use our special class to be 1171 // able to modify the item height. 1172 menu_item = new ButtonContainerMenuItemView(parent, id, height); 1173 parent->GetSubmenu()->AddChildView(menu_item); 1174 } else { 1175 // For all other cases we use the more generic way to add menu items. 1176 menu_item = parent->AppendMenuItemFromModel(model, index, id); 1177 } 1178 1179 if (menu_item) { 1180 // Flush all buttons to the right side of the menu for the new menu type. 1181 menu_item->set_use_right_margin(!use_new_menu_); 1182 menu_item->SetVisible(model->IsVisibleAt(index)); 1183 1184 if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) { 1185 gfx::Image icon; 1186 if (model->GetIconAt(index, &icon)) 1187 menu_item->SetIcon(*icon.ToImageSkia()); 1188 } 1189 } 1190 1191 return menu_item; 1192 } 1193 1194 void WrenchMenu::CancelAndEvaluate(MenuModel* model, int index) { 1195 selected_menu_model_ = model; 1196 selected_index_ = index; 1197 root_->Cancel(); 1198 } 1199 1200 void WrenchMenu::CreateBookmarkMenu() { 1201 if (bookmark_menu_delegate_.get()) 1202 return; // Already created the menu. 1203 1204 BookmarkModel* model = 1205 BookmarkModelFactory::GetForProfile(browser_->profile()); 1206 if (!model->loaded()) 1207 return; 1208 1209 model->AddObserver(this); 1210 1211 // TODO(oshima): Replace with views only API. 1212 views::Widget* parent = views::Widget::GetWidgetForNativeWindow( 1213 browser_->window()->GetNativeWindow()); 1214 bookmark_menu_delegate_.reset( 1215 new BookmarkMenuDelegate(browser_, 1216 browser_, 1217 parent, 1218 first_bookmark_command_id_)); 1219 bookmark_menu_delegate_->Init( 1220 this, bookmark_menu_, model->bookmark_bar_node(), 0, 1221 BookmarkMenuDelegate::SHOW_PERMANENT_FOLDERS, 1222 bookmark_utils::LAUNCH_WRENCH_MENU); 1223 } 1224