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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/wrench_menu.h" 6 7 #include <cmath> 8 9 #include "base/string_number_conversions.h" 10 #include "base/utf_string_conversions.h" 11 #include "chrome/app/chrome_command_ids.h" 12 #include "chrome/browser/metrics/user_metrics.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "content/browser/tab_contents/tab_contents.h" 16 #include "content/common/notification_observer.h" 17 #include "content/common/notification_registrar.h" 18 #include "content/common/notification_source.h" 19 #include "content/common/notification_type.h" 20 #include "grit/chromium_strings.h" 21 #include "grit/generated_resources.h" 22 #include "grit/theme_resources.h" 23 #include "third_party/skia/include/core/SkPaint.h" 24 #include "ui/base/l10n/l10n_util.h" 25 #include "ui/base/resource/resource_bundle.h" 26 #include "ui/gfx/canvas.h" 27 #include "ui/gfx/canvas_skia.h" 28 #include "ui/gfx/skia_util.h" 29 #include "views/background.h" 30 #include "views/controls/button/image_button.h" 31 #include "views/controls/button/menu_button.h" 32 #include "views/controls/button/text_button.h" 33 #include "views/controls/label.h" 34 #include "views/controls/menu/menu_config.h" 35 #include "views/controls/menu/menu_item_view.h" 36 #include "views/controls/menu/menu_scroll_view_container.h" 37 #include "views/controls/menu/submenu_view.h" 38 #include "views/window/window.h" 39 40 using ui::MenuModel; 41 using views::CustomButton; 42 using views::ImageButton; 43 using views::Label; 44 using views::MenuConfig; 45 using views::MenuItemView; 46 using views::TextButton; 47 using views::View; 48 49 namespace { 50 51 // Colors used for buttons. 52 const SkColor kHotBorderColor = SkColorSetARGB(72, 0, 0, 0); 53 const SkColor kBorderColor = SkColorSetARGB(36, 0, 0, 0); 54 const SkColor kPushedBorderColor = SkColorSetARGB(72, 0, 0, 0); 55 const SkColor kHotBackgroundColor = SkColorSetARGB(204, 255, 255, 255); 56 const SkColor kBackgroundColor = SkColorSetARGB(102, 255, 255, 255); 57 const SkColor kPushedBackgroundColor = SkColorSetARGB(13, 0, 0, 0); 58 59 // Horizontal padding on the edges of the buttons. 60 const int kHorizontalPadding = 6; 61 62 // Subclass of ImageButton whose preferred size includes the size of the border. 63 class FullscreenButton : public ImageButton { 64 public: 65 explicit FullscreenButton(views::ButtonListener* listener) 66 : ImageButton(listener) { } 67 68 virtual gfx::Size GetPreferredSize() { 69 gfx::Size pref = ImageButton::GetPreferredSize(); 70 gfx::Insets insets; 71 if (border()) 72 border()->GetInsets(&insets); 73 pref.Enlarge(insets.width(), insets.height()); 74 return pref; 75 } 76 77 private: 78 DISALLOW_COPY_AND_ASSIGN(FullscreenButton); 79 }; 80 81 // Border for buttons contained in the menu. This is only used for getting the 82 // insets, the actual painting is done in MenuButtonBackground. 83 class MenuButtonBorder : public views::Border { 84 public: 85 MenuButtonBorder() {} 86 87 virtual void Paint(const View& view, gfx::Canvas* canvas) const { 88 // Painting of border is done in MenuButtonBackground. 89 } 90 91 virtual void GetInsets(gfx::Insets* insets) const { 92 insets->Set(MenuConfig::instance().item_top_margin, 93 kHorizontalPadding, 94 MenuConfig::instance().item_bottom_margin, 95 kHorizontalPadding); 96 } 97 98 private: 99 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder); 100 }; 101 102 // Combination border/background for the buttons contained in the menu. The 103 // painting of the border/background is done here as TextButton does not always 104 // paint the border. 105 class MenuButtonBackground : public views::Background { 106 public: 107 enum ButtonType { 108 LEFT_BUTTON, 109 CENTER_BUTTON, 110 RIGHT_BUTTON, 111 SINGLE_BUTTON, 112 }; 113 114 explicit MenuButtonBackground(ButtonType type) 115 : type_(type), 116 left_button_(NULL), 117 right_button_(NULL) {} 118 119 // Used when the type is CENTER_BUTTON to determine if the left/right edge 120 // needs to be rendered selected. 121 void SetOtherButtons(CustomButton* left_button, CustomButton* right_button) { 122 if (base::i18n::IsRTL()) { 123 left_button_ = right_button; 124 right_button_ = left_button; 125 } else { 126 left_button_ = left_button; 127 right_button_ = right_button; 128 } 129 } 130 131 virtual void Paint(gfx::Canvas* canvas, View* view) const { 132 CustomButton::ButtonState state = 133 (view->GetClassName() == views::Label::kViewClassName) ? 134 CustomButton::BS_NORMAL : static_cast<CustomButton*>(view)->state(); 135 int w = view->width(); 136 int h = view->height(); 137 switch (TypeAdjustedForRTL()) { 138 case LEFT_BUTTON: 139 canvas->FillRectInt(background_color(state), 1, 1, w, h - 2); 140 canvas->FillRectInt(border_color(state), 2, 0, w, 1); 141 canvas->FillRectInt(border_color(state), 1, 1, 1, 1); 142 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4); 143 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1); 144 canvas->FillRectInt(border_color(state), 2, h - 1, w, 1); 145 break; 146 147 case CENTER_BUTTON: { 148 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2); 149 SkColor left_color = state != CustomButton::BS_NORMAL ? 150 border_color(state) : border_color(left_button_->state()); 151 canvas->FillRectInt(left_color, 0, 0, 1, h); 152 canvas->FillRectInt(border_color(state), 1, 0, w - 2, 1); 153 canvas->FillRectInt(border_color(state), 1, h - 1, w - 2, 1); 154 SkColor right_color = state != CustomButton::BS_NORMAL ? 155 border_color(state) : border_color(right_button_->state()); 156 canvas->FillRectInt(right_color, w - 1, 0, 1, h); 157 break; 158 } 159 160 case RIGHT_BUTTON: 161 canvas->FillRectInt(background_color(state), 0, 1, w - 1, h - 2); 162 canvas->FillRectInt(border_color(state), 0, 0, w - 2, 1); 163 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1); 164 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4); 165 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1); 166 canvas->FillRectInt(border_color(state), 0, h - 1, w - 2, 1); 167 break; 168 169 case SINGLE_BUTTON: 170 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2); 171 canvas->FillRectInt(border_color(state), 2, 0, w - 4, 1); 172 canvas->FillRectInt(border_color(state), 1, 1, 1, 1); 173 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4); 174 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1); 175 canvas->FillRectInt(border_color(state), 2, h - 1, w - 4, 1); 176 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1); 177 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4); 178 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1); 179 break; 180 181 default: 182 NOTREACHED(); 183 break; 184 } 185 } 186 187 private: 188 static SkColor border_color(CustomButton::ButtonState state) { 189 switch (state) { 190 case CustomButton::BS_HOT: return kHotBorderColor; 191 case CustomButton::BS_PUSHED: return kPushedBorderColor; 192 default: return kBorderColor; 193 } 194 } 195 196 static SkColor background_color(CustomButton::ButtonState state) { 197 switch (state) { 198 case CustomButton::BS_HOT: return kHotBackgroundColor; 199 case CustomButton::BS_PUSHED: return kPushedBackgroundColor; 200 default: return kBackgroundColor; 201 } 202 } 203 204 ButtonType TypeAdjustedForRTL() const { 205 if (!base::i18n::IsRTL()) 206 return type_; 207 208 switch (type_) { 209 case LEFT_BUTTON: return RIGHT_BUTTON; 210 case RIGHT_BUTTON: return LEFT_BUTTON; 211 default: break; 212 } 213 return type_; 214 } 215 216 const ButtonType type_; 217 218 // See description above setter for details. 219 CustomButton* left_button_; 220 CustomButton* right_button_; 221 222 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground); 223 }; 224 225 // A View subclass that forces SchedulePaint to paint all. Normally when the 226 // mouse enters/exits a button the buttons invokes SchedulePaint. As part of the 227 // button border (MenuButtonBackground) is rendered by the button to the 228 // left/right of it SchedulePaint on the the button may not be enough, so this 229 // forces a paint all. 230 class ScheduleAllView : public views::View { 231 public: 232 ScheduleAllView() {} 233 234 virtual void SchedulePaintInRect(const gfx::Rect& r) { 235 if (!IsVisible()) 236 return; 237 238 if (parent()) 239 parent()->SchedulePaintInRect(GetMirroredBounds()); 240 } 241 242 private: 243 DISALLOW_COPY_AND_ASSIGN(ScheduleAllView); 244 }; 245 246 string16 GetAccessibleNameForWrenchMenuItem( 247 MenuModel* model, int item_index, int accessible_string_id) { 248 string16 accessible_name = l10n_util::GetStringUTF16(accessible_string_id); 249 string16 accelerator_text; 250 251 ui::Accelerator menu_accelerator; 252 if (model->GetAcceleratorAt(item_index, &menu_accelerator)) { 253 accelerator_text = 254 views::Accelerator(menu_accelerator.GetKeyCode(), 255 menu_accelerator.modifiers()).GetShortcutText(); 256 } 257 258 return MenuItemView::GetAccessibleNameForMenuItem( 259 accessible_name, accelerator_text); 260 } 261 262 // WrenchMenuView is a view that can contain text buttons. 263 class WrenchMenuView : public ScheduleAllView, public views::ButtonListener { 264 public: 265 WrenchMenuView(WrenchMenu* menu, MenuModel* menu_model) 266 : menu_(menu), menu_model_(menu_model) { } 267 268 TextButton* CreateAndConfigureButton(int string_id, 269 MenuButtonBackground::ButtonType type, 270 int index, 271 MenuButtonBackground** background) { 272 return CreateButtonWithAccName( 273 string_id, type, index, background, string_id); 274 } 275 276 TextButton* CreateButtonWithAccName(int string_id, 277 MenuButtonBackground::ButtonType type, 278 int index, 279 MenuButtonBackground** background, 280 int acc_string_id) { 281 TextButton* button = 282 new TextButton(this, UTF16ToWide(l10n_util::GetStringUTF16(string_id))); 283 button->SetAccessibleName( 284 GetAccessibleNameForWrenchMenuItem(menu_model_, index, acc_string_id)); 285 button->SetFocusable(true); 286 button->set_request_focus_on_press(false); 287 button->set_tag(index); 288 button->SetEnabled(menu_model_->IsEnabledAt(index)); 289 button->set_prefix_type(TextButton::PREFIX_HIDE); 290 MenuButtonBackground* bg = new MenuButtonBackground(type); 291 button->set_background(bg); 292 button->SetEnabledColor(MenuConfig::instance().text_color); 293 if (background) 294 *background = bg; 295 button->set_border(new MenuButtonBorder()); 296 button->set_alignment(TextButton::ALIGN_CENTER); 297 button->SetNormalHasBorder(true); 298 button->SetFont(views::MenuConfig::instance().font); 299 button->ClearMaxTextSize(); 300 AddChildView(button); 301 return button; 302 } 303 304 protected: 305 // Hosting WrenchMenu. 306 WrenchMenu* menu_; 307 308 // The menu model containing the increment/decrement/reset items. 309 MenuModel* menu_model_; 310 311 private: 312 DISALLOW_COPY_AND_ASSIGN(WrenchMenuView); 313 }; 314 315 } // namespace 316 317 // CutCopyPasteView ------------------------------------------------------------ 318 319 // CutCopyPasteView is the view containing the cut/copy/paste buttons. 320 class WrenchMenu::CutCopyPasteView : public WrenchMenuView { 321 public: 322 CutCopyPasteView(WrenchMenu* menu, 323 MenuModel* menu_model, 324 int cut_index, 325 int copy_index, 326 int paste_index) 327 : WrenchMenuView(menu, menu_model) { 328 TextButton* cut = CreateAndConfigureButton( 329 IDS_CUT, MenuButtonBackground::LEFT_BUTTON, cut_index, NULL); 330 331 MenuButtonBackground* copy_background = NULL; 332 CreateAndConfigureButton( 333 IDS_COPY, MenuButtonBackground::CENTER_BUTTON, copy_index, 334 ©_background); 335 336 TextButton* paste = CreateAndConfigureButton( 337 IDS_PASTE, MenuButtonBackground::RIGHT_BUTTON, paste_index, NULL); 338 339 copy_background->SetOtherButtons(cut, paste); 340 } 341 342 gfx::Size GetPreferredSize() { 343 // Returned height doesn't matter as MenuItemView forces everything to the 344 // height of the menuitemview. 345 return gfx::Size(GetMaxChildViewPreferredWidth() * child_count(), 0); 346 } 347 348 void Layout() { 349 // All buttons are given the same width. 350 int width = GetMaxChildViewPreferredWidth(); 351 for (int i = 0; i < child_count(); ++i) 352 GetChildViewAt(i)->SetBounds(i * width, 0, width, height()); 353 } 354 355 // ButtonListener 356 virtual void ButtonPressed(views::Button* sender, const views::Event& event) { 357 menu_->CancelAndEvaluate(menu_model_, sender->tag()); 358 } 359 360 private: 361 // Returns the max preferred width of all the children. 362 int GetMaxChildViewPreferredWidth() { 363 int width = 0; 364 for (int i = 0; i < child_count(); ++i) 365 width = std::max(width, GetChildViewAt(i)->GetPreferredSize().width()); 366 return width; 367 } 368 369 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView); 370 }; 371 372 // ZoomView -------------------------------------------------------------------- 373 374 // Padding between the increment buttons and the reset button. 375 static const int kZoomPadding = 6; 376 377 // ZoomView contains the various zoom controls: two buttons to increase/decrease 378 // the zoom, a label showing the current zoom percent, and a button to go 379 // full-screen. 380 class WrenchMenu::ZoomView : public WrenchMenuView, 381 public NotificationObserver { 382 public: 383 ZoomView(WrenchMenu* menu, 384 MenuModel* menu_model, 385 int decrement_index, 386 int increment_index, 387 int fullscreen_index) 388 : WrenchMenuView(menu, menu_model), 389 fullscreen_index_(fullscreen_index), 390 increment_button_(NULL), 391 zoom_label_(NULL), 392 decrement_button_(NULL), 393 fullscreen_button_(NULL), 394 zoom_label_width_(0) { 395 decrement_button_ = CreateButtonWithAccName( 396 IDS_ZOOM_MINUS2, MenuButtonBackground::LEFT_BUTTON, decrement_index, 397 NULL, IDS_ACCNAME_ZOOM_MINUS2); 398 399 zoom_label_ = new Label( 400 UTF16ToWide(l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100))); 401 zoom_label_->SetColor(MenuConfig::instance().text_color); 402 zoom_label_->SetHorizontalAlignment(Label::ALIGN_RIGHT); 403 MenuButtonBackground* center_bg = 404 new MenuButtonBackground(MenuButtonBackground::CENTER_BUTTON); 405 zoom_label_->set_background(center_bg); 406 zoom_label_->set_border(new MenuButtonBorder()); 407 zoom_label_->SetFont(MenuConfig::instance().font); 408 AddChildView(zoom_label_); 409 zoom_label_width_ = MaxWidthForZoomLabel(); 410 411 increment_button_ = CreateButtonWithAccName( 412 IDS_ZOOM_PLUS2, MenuButtonBackground::RIGHT_BUTTON, increment_index, 413 NULL, IDS_ACCNAME_ZOOM_PLUS2); 414 415 center_bg->SetOtherButtons(decrement_button_, increment_button_); 416 417 fullscreen_button_ = new FullscreenButton(this); 418 fullscreen_button_->SetImage( 419 ImageButton::BS_NORMAL, 420 ResourceBundle::GetSharedInstance().GetBitmapNamed( 421 IDR_FULLSCREEN_MENU_BUTTON)); 422 fullscreen_button_->SetFocusable(true); 423 fullscreen_button_->set_request_focus_on_press(false); 424 fullscreen_button_->set_tag(fullscreen_index); 425 fullscreen_button_->SetImageAlignment( 426 ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE); 427 fullscreen_button_->set_border(views::Border::CreateEmptyBorder( 428 0, kHorizontalPadding, 0, kHorizontalPadding)); 429 fullscreen_button_->set_background( 430 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON)); 431 fullscreen_button_->SetAccessibleName( 432 GetAccessibleNameForWrenchMenuItem( 433 menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN)); 434 AddChildView(fullscreen_button_); 435 436 UpdateZoomControls(); 437 438 registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED, 439 Source<Profile>(menu->browser_->profile())); 440 } 441 442 gfx::Size GetPreferredSize() { 443 // The increment/decrement button are forced to the same width. 444 int button_width = std::max(increment_button_->GetPreferredSize().width(), 445 decrement_button_->GetPreferredSize().width()); 446 int fullscreen_width = fullscreen_button_->GetPreferredSize().width(); 447 // Returned height doesn't matter as MenuItemView forces everything to the 448 // height of the menuitemview. 449 return gfx::Size(button_width + zoom_label_width_ + button_width + 450 kZoomPadding + fullscreen_width, 0); 451 } 452 453 void Layout() { 454 int x = 0; 455 int button_width = std::max(increment_button_->GetPreferredSize().width(), 456 decrement_button_->GetPreferredSize().width()); 457 gfx::Rect bounds(0, 0, button_width, height()); 458 459 decrement_button_->SetBoundsRect(bounds); 460 461 x += bounds.width(); 462 bounds.set_x(x); 463 bounds.set_width(zoom_label_width_); 464 zoom_label_->SetBoundsRect(bounds); 465 466 x += bounds.width(); 467 bounds.set_x(x); 468 bounds.set_width(button_width); 469 increment_button_->SetBoundsRect(bounds); 470 471 x += bounds.width() + kZoomPadding; 472 bounds.set_x(x); 473 bounds.set_width(fullscreen_button_->GetPreferredSize().width()); 474 fullscreen_button_->SetBoundsRect(bounds); 475 } 476 477 // ButtonListener: 478 virtual void ButtonPressed(views::Button* sender, const views::Event& event) { 479 if (sender->tag() == fullscreen_index_) { 480 menu_->CancelAndEvaluate(menu_model_, sender->tag()); 481 } else { 482 // Zoom buttons don't close the menu. 483 menu_model_->ActivatedAt(sender->tag()); 484 } 485 } 486 487 // NotificationObserver: 488 virtual void Observe(NotificationType type, 489 const NotificationSource& source, 490 const NotificationDetails& details) { 491 DCHECK_EQ(NotificationType::ZOOM_LEVEL_CHANGED, type.value); 492 UpdateZoomControls(); 493 } 494 495 private: 496 void UpdateZoomControls() { 497 bool enable_increment = false; 498 bool enable_decrement = false; 499 TabContents* selected_tab = menu_->browser_->GetSelectedTabContents(); 500 int zoom = 100; 501 if (selected_tab) 502 zoom = selected_tab->GetZoomPercent(&enable_increment, &enable_decrement); 503 increment_button_->SetEnabled(enable_increment); 504 decrement_button_->SetEnabled(enable_decrement); 505 zoom_label_->SetText(UTF16ToWide(l10n_util::GetStringFUTF16Int( 506 IDS_ZOOM_PERCENT, 507 zoom))); 508 509 zoom_label_width_ = MaxWidthForZoomLabel(); 510 } 511 512 // Calculates the max width the zoom string can be. 513 int MaxWidthForZoomLabel() { 514 gfx::Font font = zoom_label_->font(); 515 gfx::Insets insets; 516 if (zoom_label_->border()) 517 zoom_label_->border()->GetInsets(&insets); 518 519 int max_w = 0; 520 521 TabContents* selected_tab = menu_->browser_->GetSelectedTabContents(); 522 if (selected_tab) { 523 int min_percent = selected_tab->minimum_zoom_percent(); 524 int max_percent = selected_tab->maximum_zoom_percent(); 525 526 int step = (max_percent - min_percent) / 10; 527 for (int i = min_percent; i <= max_percent; i += step) { 528 int w = font.GetStringWidth( 529 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, i)); 530 max_w = std::max(w, max_w); 531 } 532 } else { 533 max_w = font.GetStringWidth( 534 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100)); 535 } 536 537 return max_w + insets.width(); 538 } 539 540 // Index of the fullscreen menu item in the model. 541 const int fullscreen_index_; 542 543 NotificationRegistrar registrar_; 544 545 // Button for incrementing the zoom. 546 TextButton* increment_button_; 547 548 // Label showing zoom as a percent. 549 Label* zoom_label_; 550 551 // Button for decrementing the zoom. 552 TextButton* decrement_button_; 553 554 ImageButton* fullscreen_button_; 555 556 // Width given to |zoom_label_|. This is the width at 100%. 557 int zoom_label_width_; 558 559 DISALLOW_COPY_AND_ASSIGN(ZoomView); 560 }; 561 562 // WrenchMenu ------------------------------------------------------------------ 563 564 WrenchMenu::WrenchMenu(Browser* browser) 565 : browser_(browser), 566 selected_menu_model_(NULL), 567 selected_index_(0) { 568 } 569 570 void WrenchMenu::Init(ui::MenuModel* model) { 571 DCHECK(!root_.get()); 572 root_.reset(new MenuItemView(this)); 573 root_->set_has_icons(true); // We have checks, radios and icons, set this 574 // so we get the taller menu style. 575 int next_id = 1; 576 PopulateMenu(root_.get(), model, &next_id); 577 } 578 579 void WrenchMenu::RunMenu(views::MenuButton* host) { 580 // Up the ref count while the menu is displaying. This way if the window is 581 // deleted while we're running we won't prematurely delete the menu. 582 // TODO(sky): fix this, the menu should really take ownership of the menu 583 // (57890). 584 scoped_refptr<WrenchMenu> dont_delete_while_running(this); 585 gfx::Point screen_loc; 586 views::View::ConvertPointToScreen(host, &screen_loc); 587 gfx::Rect bounds(screen_loc, host->size()); 588 UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu")); 589 root_->RunMenuAt(host->GetWindow()->GetNativeWindow(), host, bounds, 590 base::i18n::IsRTL() ? MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT, 591 true); 592 if (selected_menu_model_) 593 selected_menu_model_->ActivatedAt(selected_index_); 594 } 595 596 bool WrenchMenu::IsItemChecked(int id) const { 597 const Entry& entry = id_to_entry_.find(id)->second; 598 return entry.first->IsItemCheckedAt(entry.second); 599 } 600 601 bool WrenchMenu::IsCommandEnabled(int id) const { 602 if (id == 0) 603 return false; // The root item. 604 605 const Entry& entry = id_to_entry_.find(id)->second; 606 int command_id = entry.first->GetCommandIdAt(entry.second); 607 // The items representing the cut (cut/copy/paste) and zoom menu 608 // (increment/decrement/reset) are always enabled. The child views of these 609 // items enabled state updates appropriately. 610 return command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS || 611 entry.first->IsEnabledAt(entry.second); 612 } 613 614 void WrenchMenu::ExecuteCommand(int id) { 615 const Entry& entry = id_to_entry_.find(id)->second; 616 int command_id = entry.first->GetCommandIdAt(entry.second); 617 618 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { 619 // These items are represented by child views. If ExecuteCommand is invoked 620 // it means the user clicked on the area around the buttons and we should 621 // not do anyting. 622 return; 623 } 624 625 return entry.first->ActivatedAt(entry.second); 626 } 627 628 bool WrenchMenu::GetAccelerator(int id, views::Accelerator* accelerator) { 629 const Entry& entry = id_to_entry_.find(id)->second; 630 int command_id = entry.first->GetCommandIdAt(entry.second); 631 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { 632 // These have special child views; don't show the accelerator for them. 633 return false; 634 } 635 636 ui::Accelerator menu_accelerator; 637 if (!entry.first->GetAcceleratorAt(entry.second, &menu_accelerator)) 638 return false; 639 640 *accelerator = views::Accelerator(menu_accelerator.GetKeyCode(), 641 menu_accelerator.modifiers()); 642 return true; 643 } 644 645 WrenchMenu::~WrenchMenu() { 646 } 647 648 void WrenchMenu::PopulateMenu(MenuItemView* parent, 649 MenuModel* model, 650 int* next_id) { 651 int index_offset = model->GetFirstItemIndex(NULL); 652 for (int i = 0, max = model->GetItemCount(); i < max; ++i) { 653 int index = i + index_offset; 654 655 MenuItemView* item = 656 AppendMenuItem(parent, model, index, model->GetTypeAt(index), next_id); 657 658 if (model->GetTypeAt(index) == MenuModel::TYPE_SUBMENU) 659 PopulateMenu(item, model->GetSubmenuModelAt(index), next_id); 660 661 if (model->GetCommandIdAt(index) == IDC_CUT) { 662 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index)); 663 DCHECK_LT(i + 2, max); 664 DCHECK_EQ(IDC_COPY, model->GetCommandIdAt(index + 1)); 665 DCHECK_EQ(IDC_PASTE, model->GetCommandIdAt(index + 2)); 666 item->SetTitle(UTF16ToWide(l10n_util::GetStringUTF16(IDS_EDIT2))); 667 item->AddChildView( 668 new CutCopyPasteView(this, model, index, index + 1, index + 2)); 669 i += 2; 670 } else if (model->GetCommandIdAt(index) == IDC_ZOOM_MINUS) { 671 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index)); 672 DCHECK_EQ(IDC_ZOOM_PLUS, model->GetCommandIdAt(index + 1)); 673 DCHECK_EQ(IDC_FULLSCREEN, model->GetCommandIdAt(index + 2)); 674 item->SetTitle(UTF16ToWide(l10n_util::GetStringUTF16(IDS_ZOOM_MENU2))); 675 item->AddChildView( 676 new ZoomView(this, model, index, index + 1, index + 2)); 677 i += 2; 678 } 679 } 680 } 681 682 MenuItemView* WrenchMenu::AppendMenuItem(MenuItemView* parent, 683 MenuModel* model, 684 int index, 685 MenuModel::ItemType menu_type, 686 int* next_id) { 687 int id = (*next_id)++; 688 689 id_to_entry_[id].first = model; 690 id_to_entry_[id].second = index; 691 692 MenuItemView* menu_item = parent->AppendMenuItemFromModel(model, index, id); 693 694 if (menu_item) 695 menu_item->SetVisible(model->IsVisibleAt(index)); 696 697 if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) { 698 SkBitmap icon; 699 if (model->GetIconAt(index, &icon)) 700 menu_item->SetIcon(icon); 701 } 702 703 return menu_item; 704 } 705 706 void WrenchMenu::CancelAndEvaluate(MenuModel* model, int index) { 707 selected_menu_model_ = model; 708 selected_index_ = index; 709 root_->Cancel(); 710 } 711