1 // Copyright 2013 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.
5 #include "chrome/browser/ui/views/toolbar/wrench_menu.h"
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_factory.h"
15 #include "chrome/browser/bookmarks/bookmark_stats.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/toolbar/wrench_menu_model.h"
23 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
24 #include "chrome/browser/ui/views/toolbar/wrench_menu_observer.h"
25 #include "components/bookmarks/browser/bookmark_model.h"
26 #include "content/public/browser/host_zoom_map.h"
27 #include "content/public/browser/notification_observer.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/user_metrics.h"
32 #include "content/public/browser/web_contents.h"
33 #include "grit/chromium_strings.h"
34 #include "grit/generated_resources.h"
35 #include "grit/theme_resources.h"
36 #include "third_party/skia/include/core/SkCanvas.h"
37 #include "third_party/skia/include/core/SkPaint.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/layout.h"
40 #include "ui/base/resource/resource_bundle.h"
41 #include "ui/gfx/canvas.h"
42 #include "ui/gfx/font_list.h"
43 #include "ui/gfx/image/image.h"
44 #include "ui/gfx/image/image_skia_source.h"
45 #include "ui/gfx/skia_util.h"
46 #include "ui/gfx/text_utils.h"
47 #include "ui/views/background.h"
48 #include "ui/views/controls/button/image_button.h"
49 #include "ui/views/controls/button/label_button.h"
50 #include "ui/views/controls/button/menu_button.h"
51 #include "ui/views/controls/label.h"
52 #include "ui/views/controls/menu/menu_config.h"
53 #include "ui/views/controls/menu/menu_item_view.h"
54 #include "ui/views/controls/menu/menu_model_adapter.h"
55 #include "ui/views/controls/menu/menu_runner.h"
56 #include "ui/views/controls/menu/menu_scroll_view_container.h"
57 #include "ui/views/controls/menu/submenu_view.h"
58 #include "ui/views/widget/widget.h"
60 using base::UserMetricsAction
;
61 using content::HostZoomMap
;
62 using content::WebContents
;
64 using views::CustomButton
;
65 using views::ImageButton
;
67 using views::LabelButton
;
68 using views::MenuConfig
;
69 using views::MenuItemView
;
74 // Horizontal padding on the edges of the buttons.
75 const int kHorizontalPadding
= 6;
76 // Horizontal padding for a touch enabled menu.
77 const int kHorizontalTouchPadding
= 15;
79 // Menu items which have embedded buttons should have this height in pixel.
80 const int kMenuItemContainingButtonsHeight
= 43;
82 // Returns true if |command_id| identifies a bookmark menu item.
83 bool IsBookmarkCommand(int command_id
) {
84 return command_id
>= WrenchMenuModel::kMinBookmarkCommandId
&&
85 command_id
<= WrenchMenuModel::kMaxBookmarkCommandId
;
88 // Returns true if |command_id| identifies a recent tabs menu item.
89 bool IsRecentTabsCommand(int command_id
) {
90 return command_id
>= WrenchMenuModel::kMinRecentTabsCommandId
&&
91 command_id
<= WrenchMenuModel::kMaxRecentTabsCommandId
;
94 // Subclass of ImageButton whose preferred size includes the size of the border.
95 class FullscreenButton
: public ImageButton
{
97 explicit FullscreenButton(views::ButtonListener
* listener
)
98 : ImageButton(listener
) { }
100 // Overridden from ImageButton.
101 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
102 gfx::Size pref
= ImageButton::GetPreferredSize();
104 gfx::Insets insets
= border()->GetInsets();
105 pref
.Enlarge(insets
.width(), insets
.height());
111 DISALLOW_COPY_AND_ASSIGN(FullscreenButton
);
114 // Border for buttons contained in the menu. This is only used for getting the
115 // insets, the actual painting is done in MenuButtonBackground.
116 class MenuButtonBorder
: public views::Border
{
118 MenuButtonBorder(const MenuConfig
& config
, bool use_new_menu
)
119 : horizontal_padding_(use_new_menu
?
120 kHorizontalTouchPadding
: kHorizontalPadding
),
121 insets_(config
.item_top_margin
, horizontal_padding_
,
122 config
.item_bottom_margin
, horizontal_padding_
) {
125 // Overridden from views::Border.
126 virtual void Paint(const View
& view
, gfx::Canvas
* canvas
) OVERRIDE
{
127 // Painting of border is done in MenuButtonBackground.
130 virtual gfx::Insets
GetInsets() const OVERRIDE
{
134 virtual gfx::Size
GetMinimumSize() const OVERRIDE
{
135 // This size is sufficient for MenuButtonBackground::Paint() to draw any of
137 return gfx::Size(4, 4);
141 // The horizontal padding dependent on the layout.
142 const int horizontal_padding_
;
144 const gfx::Insets insets_
;
146 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder
);
149 // Combination border/background for the buttons contained in the menu. The
150 // painting of the border/background is done here as TextButton does not always
152 class MenuButtonBackground
: public views::Background
{
161 MenuButtonBackground(ButtonType type
, bool use_new_menu
)
163 use_new_menu_(use_new_menu
),
165 right_button_(NULL
) {}
167 // Used when the type is CENTER_BUTTON to determine if the left/right edge
168 // needs to be rendered selected.
169 void SetOtherButtons(CustomButton
* left_button
, CustomButton
* right_button
) {
170 if (base::i18n::IsRTL()) {
171 left_button_
= right_button
;
172 right_button_
= left_button
;
174 left_button_
= left_button
;
175 right_button_
= right_button
;
179 // Overridden from views::Background.
180 virtual void Paint(gfx::Canvas
* canvas
, View
* view
) const OVERRIDE
{
181 CustomButton
* button
= CustomButton::AsCustomButton(view
);
182 views::Button::ButtonState state
=
183 button
? button
->state() : views::Button::STATE_NORMAL
;
184 int w
= view
->width();
185 int h
= view
->height();
187 // Normal buttons get a border drawn on the right side and the rest gets
188 // filled in. The left button however does not get a line to combine
190 if (type_
!= RIGHT_BUTTON
) {
191 canvas
->FillRect(gfx::Rect(0, 0, 1, h
),
192 BorderColor(view
, views::Button::STATE_NORMAL
));
194 gfx::Rect
bounds(view
->GetLocalBounds());
195 bounds
.set_x(view
->GetMirroredXForRect(bounds
));
196 DrawBackground(canvas
, view
, bounds
, state
);
199 const SkColor border_color
= BorderColor(view
, state
);
200 switch (TypeAdjustedForRTL()) {
201 // TODO(pkasting): Why don't all the following use SkPaths with rounded
204 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
, h
- 2), state
);
205 canvas
->FillRect(gfx::Rect(2, 0, w
, 1), border_color
);
206 canvas
->FillRect(gfx::Rect(1, 1, 1, 1), border_color
);
207 canvas
->FillRect(gfx::Rect(0, 2, 1, h
- 4), border_color
);
208 canvas
->FillRect(gfx::Rect(1, h
- 2, 1, 1), border_color
);
209 canvas
->FillRect(gfx::Rect(2, h
- 1, w
, 1), border_color
);
212 case CENTER_BUTTON
: {
213 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
- 2, h
- 2), state
);
214 SkColor left_color
= state
!= views::Button::STATE_NORMAL
?
215 border_color
: BorderColor(view
, left_button_
->state());
216 canvas
->FillRect(gfx::Rect(0, 0, 1, h
), left_color
);
217 canvas
->FillRect(gfx::Rect(1, 0, w
- 2, 1), border_color
);
218 canvas
->FillRect(gfx::Rect(1, h
- 1, w
- 2, 1),
220 SkColor right_color
= state
!= views::Button::STATE_NORMAL
?
221 border_color
: BorderColor(view
, right_button_
->state());
222 canvas
->FillRect(gfx::Rect(w
- 1, 0, 1, h
), right_color
);
227 DrawBackground(canvas
, view
, gfx::Rect(0, 1, w
- 1, h
- 2), state
);
228 canvas
->FillRect(gfx::Rect(0, 0, w
- 2, 1), border_color
);
229 canvas
->FillRect(gfx::Rect(w
- 2, 1, 1, 1), border_color
);
230 canvas
->FillRect(gfx::Rect(w
- 1, 2, 1, h
- 4), border_color
);
231 canvas
->FillRect(gfx::Rect(w
- 2, h
- 2, 1, 1), border_color
);
232 canvas
->FillRect(gfx::Rect(0, h
- 1, w
- 2, 1), border_color
);
236 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
- 2, h
- 2), state
);
237 canvas
->FillRect(gfx::Rect(2, 0, w
- 4, 1), border_color
);
238 canvas
->FillRect(gfx::Rect(1, 1, 1, 1), border_color
);
239 canvas
->FillRect(gfx::Rect(0, 2, 1, h
- 4), border_color
);
240 canvas
->FillRect(gfx::Rect(1, h
- 2, 1, 1), border_color
);
241 canvas
->FillRect(gfx::Rect(2, h
- 1, w
- 4, 1), border_color
);
242 canvas
->FillRect(gfx::Rect(w
- 2, 1, 1, 1), border_color
);
243 canvas
->FillRect(gfx::Rect(w
- 1, 2, 1, h
- 4), border_color
);
244 canvas
->FillRect(gfx::Rect(w
- 2, h
- 2, 1, 1), border_color
);
254 static SkColor
BorderColor(View
* view
, views::Button::ButtonState state
) {
255 ui::NativeTheme
* theme
= view
->GetNativeTheme();
257 case views::Button::STATE_HOVERED
:
258 return theme
->GetSystemColor(
259 ui::NativeTheme::kColorId_HoverMenuButtonBorderColor
);
260 case views::Button::STATE_PRESSED
:
261 return theme
->GetSystemColor(
262 ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor
);
264 return theme
->GetSystemColor(
265 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
);
269 static SkColor
BackgroundColor(const View
* view
,
270 views::Button::ButtonState state
) {
271 const ui::NativeTheme
* theme
= view
->GetNativeTheme();
273 case views::Button::STATE_HOVERED
:
274 // Hovered should be handled in DrawBackground.
276 return theme
->GetSystemColor(
277 ui::NativeTheme::kColorId_HoverMenuItemBackgroundColor
);
278 case views::Button::STATE_PRESSED
:
279 return theme
->GetSystemColor(
280 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor
);
282 return theme
->GetSystemColor(
283 ui::NativeTheme::kColorId_MenuBackgroundColor
);
287 void DrawBackground(gfx::Canvas
* canvas
,
288 const views::View
* view
,
289 const gfx::Rect
& bounds
,
290 views::Button::ButtonState state
) const {
291 if (state
== views::Button::STATE_HOVERED
||
292 state
== views::Button::STATE_PRESSED
) {
293 view
->GetNativeTheme()->Paint(canvas
->sk_canvas(),
294 ui::NativeTheme::kMenuItemBackground
,
295 ui::NativeTheme::kHovered
,
297 ui::NativeTheme::ExtraParams());
302 canvas
->FillRect(bounds
, BackgroundColor(view
, state
));
305 ButtonType
TypeAdjustedForRTL() const {
306 if (!base::i18n::IsRTL())
310 case LEFT_BUTTON
: return RIGHT_BUTTON
;
311 case RIGHT_BUTTON
: return LEFT_BUTTON
;
317 const ButtonType type_
;
318 const bool use_new_menu_
;
320 // See description above setter for details.
321 CustomButton
* left_button_
;
322 CustomButton
* right_button_
;
324 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground
);
327 base::string16
GetAccessibleNameForWrenchMenuItem(
328 MenuModel
* model
, int item_index
, int accessible_string_id
) {
329 base::string16 accessible_name
=
330 l10n_util::GetStringUTF16(accessible_string_id
);
331 base::string16 accelerator_text
;
333 ui::Accelerator menu_accelerator
;
334 if (model
->GetAcceleratorAt(item_index
, &menu_accelerator
)) {
336 ui::Accelerator(menu_accelerator
.key_code(),
337 menu_accelerator
.modifiers()).GetShortcutText();
340 return MenuItemView::GetAccessibleNameForMenuItem(
341 accessible_name
, accelerator_text
);
344 // WrenchMenuView is a view that can contain label buttons.
345 class WrenchMenuView
: public views::View
,
346 public views::ButtonListener
,
347 public WrenchMenuObserver
{
349 WrenchMenuView(WrenchMenu
* menu
, MenuModel
* menu_model
)
351 menu_model_(menu_model
) {
352 menu_
->AddObserver(this);
355 virtual ~WrenchMenuView() {
357 menu_
->RemoveObserver(this);
360 // Overridden from views::View.
361 virtual void SchedulePaintInRect(const gfx::Rect
& r
) OVERRIDE
{
362 // Normally when the mouse enters/exits a button the buttons invokes
363 // SchedulePaint. As part of the button border (MenuButtonBackground) is
364 // rendered by the button to the left/right of it SchedulePaint on the the
365 // button may not be enough, so this forces a paint all.
366 View::SchedulePaintInRect(gfx::Rect(size()));
369 LabelButton
* CreateAndConfigureButton(const ui::NativeTheme
* native_theme
,
371 MenuButtonBackground::ButtonType type
,
373 MenuButtonBackground
** background
) {
374 return CreateButtonWithAccName(
375 native_theme
, string_id
, type
, index
, background
, string_id
);
378 LabelButton
* CreateButtonWithAccName(const ui::NativeTheme
* native_theme
,
380 MenuButtonBackground::ButtonType type
,
382 MenuButtonBackground
** background
,
384 // Should only be invoked during construction when |menu_| is valid.
386 LabelButton
* button
= new LabelButton(this, gfx::RemoveAcceleratorChar(
387 l10n_util::GetStringUTF16(string_id
), '&', NULL
, NULL
));
388 button
->SetAccessibleName(
389 GetAccessibleNameForWrenchMenuItem(menu_model_
, index
, acc_string_id
));
390 button
->SetFocusable(true);
391 button
->set_request_focus_on_press(false);
392 button
->set_tag(index
);
393 button
->SetEnabled(menu_model_
->IsEnabledAt(index
));
394 MenuButtonBackground
* bg
=
395 new MenuButtonBackground(type
, menu_
->use_new_menu());
396 button
->set_background(bg
);
397 const MenuConfig
& menu_config
= menu_
->GetMenuConfig();
400 button
->SetBorder(scoped_ptr
<views::Border
>(
401 new MenuButtonBorder(menu_config
, menu_
->use_new_menu())));
402 button
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
403 button
->SetFontList(menu_config
.font_list
);
404 button
->SetTextColor(
405 views::Button::STATE_DISABLED
,
406 native_theme
->GetSystemColor(
407 ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor
));
408 button
->SetTextColor(
409 views::Button::STATE_HOVERED
,
410 native_theme
->GetSystemColor(
411 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
));
412 button
->SetTextColor(
413 views::Button::STATE_PRESSED
,
414 native_theme
->GetSystemColor(
415 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
));
416 button
->SetTextColor(
417 views::Button::STATE_NORMAL
,
418 native_theme
->GetSystemColor(
419 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor
));
420 AddChildView(button
);
421 // all buttons on menu should must be a custom button in order for
422 // the keyboard nativigation work.
423 DCHECK(CustomButton::AsCustomButton(button
));
427 // Overridden from WrenchMenuObserver:
428 virtual void WrenchMenuDestroyed() OVERRIDE
{
429 menu_
->RemoveObserver(this);
435 WrenchMenu
* menu() { return menu_
; }
436 MenuModel
* menu_model() { return menu_model_
; }
438 bool use_new_menu() const { return menu_
->use_new_menu(); }
441 // Hosting WrenchMenu.
442 // WARNING: this may be NULL during shutdown.
445 // The menu model containing the increment/decrement/reset items.
446 // WARNING: this may be NULL during shutdown.
447 MenuModel
* menu_model_
;
449 DISALLOW_COPY_AND_ASSIGN(WrenchMenuView
);
452 class ButtonContainerMenuItemView
: public MenuItemView
{
454 // Constructor for use with button containing menu items which have a
455 // different height then normal items.
456 ButtonContainerMenuItemView(MenuItemView
* parent
, int command_id
, int height
)
457 : MenuItemView(parent
, command_id
, MenuItemView::NORMAL
),
461 // Overridden from MenuItemView.
462 virtual gfx::Size
GetChildPreferredSize() const OVERRIDE
{
463 gfx::Size size
= MenuItemView::GetChildPreferredSize();
464 // When there is a height override given, we need to deduct our spacing
465 // above and below to get to the correct height to return here for the
467 int height
= height_
- GetTopMargin() - GetBottomMargin();
468 if (height
> size
.height())
469 size
.set_height(height
);
476 DISALLOW_COPY_AND_ASSIGN(ButtonContainerMenuItemView
);
479 // Generate the button image for hover state.
480 class HoveredImageSource
: public gfx::ImageSkiaSource
{
482 HoveredImageSource(const gfx::ImageSkia
& image
, SkColor color
)
486 virtual ~HoveredImageSource() {}
488 virtual gfx::ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
489 const gfx::ImageSkiaRep
& rep
= image_
.GetRepresentation(scale
);
490 SkBitmap bitmap
= rep
.sk_bitmap();
492 white
.setConfig(SkBitmap::kARGB_8888_Config
,
493 bitmap
.width(), bitmap
.height(), 0);
495 white
.eraseARGB(0, 0, 0, 0);
497 for (int y
= 0; y
< bitmap
.height(); ++y
) {
498 uint32
* image_row
= bitmap
.getAddr32(0, y
);
499 uint32
* dst_row
= white
.getAddr32(0, y
);
500 for (int x
= 0; x
< bitmap
.width(); ++x
) {
501 uint32 image_pixel
= image_row
[x
];
502 // Fill the non transparent pixels with |color_|.
503 dst_row
[x
] = (image_pixel
& 0xFF000000) == 0x0 ? 0x0 : color_
;
506 bitmap
.unlockPixels();
507 return gfx::ImageSkiaRep(white
, scale
);
511 const gfx::ImageSkia image_
;
512 const SkColor color_
;
513 DISALLOW_COPY_AND_ASSIGN(HoveredImageSource
);
518 // CutCopyPasteView ------------------------------------------------------------
520 // CutCopyPasteView is the view containing the cut/copy/paste buttons.
521 class WrenchMenu::CutCopyPasteView
: public WrenchMenuView
{
523 CutCopyPasteView(WrenchMenu
* menu
,
524 MenuModel
* menu_model
,
525 const ui::NativeTheme
* native_theme
,
529 : WrenchMenuView(menu
, menu_model
) {
530 LabelButton
* cut
= CreateAndConfigureButton(
531 native_theme
, IDS_CUT
, MenuButtonBackground::LEFT_BUTTON
,
533 MenuButtonBackground
* copy_background
= NULL
;
534 CreateAndConfigureButton(
535 native_theme
, IDS_COPY
, MenuButtonBackground::CENTER_BUTTON
,
536 copy_index
, ©_background
);
537 LabelButton
* paste
= CreateAndConfigureButton(
540 menu
->use_new_menu() && menu
->supports_new_separators_
?
541 MenuButtonBackground::CENTER_BUTTON
:
542 MenuButtonBackground::RIGHT_BUTTON
,
545 copy_background
->SetOtherButtons(cut
, paste
);
548 // Overridden from View.
549 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
550 // Returned height doesn't matter as MenuItemView forces everything to the
551 // height of the menuitemview.
552 return gfx::Size(GetMaxChildViewPreferredWidth() * child_count(), 0);
555 virtual void Layout() OVERRIDE
{
556 // All buttons are given the same width.
557 int width
= GetMaxChildViewPreferredWidth();
558 for (int i
= 0; i
< child_count(); ++i
)
559 child_at(i
)->SetBounds(i
* width
, 0, width
, height());
562 // Overridden from ButtonListener.
563 virtual void ButtonPressed(views::Button
* sender
,
564 const ui::Event
& event
) OVERRIDE
{
565 menu()->CancelAndEvaluate(menu_model(), sender
->tag());
569 // Returns the max preferred width of all the children.
570 int GetMaxChildViewPreferredWidth() const {
572 for (int i
= 0; i
< child_count(); ++i
)
573 width
= std::max(width
, child_at(i
)->GetPreferredSize().width());
577 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView
);
580 // ZoomView --------------------------------------------------------------------
582 // Padding between the increment buttons and the reset button.
583 static const int kZoomPadding
= 6;
584 static const int kTouchZoomPadding
= 14;
586 // ZoomView contains the various zoom controls: two buttons to increase/decrease
587 // the zoom, a label showing the current zoom percent, and a button to go
589 class WrenchMenu::ZoomView
: public WrenchMenuView
{
591 ZoomView(WrenchMenu
* menu
,
592 MenuModel
* menu_model
,
593 const ui::NativeTheme
* native_theme
,
596 int fullscreen_index
)
597 : WrenchMenuView(menu
, menu_model
),
598 fullscreen_index_(fullscreen_index
),
599 increment_button_(NULL
),
601 decrement_button_(NULL
),
602 fullscreen_button_(NULL
),
603 zoom_label_width_(0) {
604 zoom_subscription_
= HostZoomMap::GetForBrowserContext(
605 menu
->browser_
->profile())->AddZoomLevelChangedCallback(
606 base::Bind(&WrenchMenu::ZoomView::OnZoomLevelChanged
,
607 base::Unretained(this)));
609 decrement_button_
= CreateButtonWithAccName(
610 native_theme
, IDS_ZOOM_MINUS2
, MenuButtonBackground::LEFT_BUTTON
,
611 decrement_index
, NULL
, IDS_ACCNAME_ZOOM_MINUS2
);
613 zoom_label_
= new Label(
614 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, 100));
615 zoom_label_
->SetAutoColorReadabilityEnabled(false);
616 zoom_label_
->SetHorizontalAlignment(gfx::ALIGN_RIGHT
);
618 MenuButtonBackground
* center_bg
= new MenuButtonBackground(
619 menu
->use_new_menu() && menu
->supports_new_separators_
?
620 MenuButtonBackground::RIGHT_BUTTON
:
621 MenuButtonBackground::CENTER_BUTTON
,
622 menu
->use_new_menu());
623 zoom_label_
->set_background(center_bg
);
624 const MenuConfig
& menu_config(menu
->GetMenuConfig());
625 zoom_label_
->SetBorder(scoped_ptr
<views::Border
>(
626 new MenuButtonBorder(menu_config
, menu
->use_new_menu())));
627 zoom_label_
->SetFontList(menu_config
.font_list
);
629 AddChildView(zoom_label_
);
630 zoom_label_width_
= MaxWidthForZoomLabel();
632 increment_button_
= CreateButtonWithAccName(
633 native_theme
, IDS_ZOOM_PLUS2
, MenuButtonBackground::RIGHT_BUTTON
,
634 increment_index
, NULL
, IDS_ACCNAME_ZOOM_PLUS2
);
636 center_bg
->SetOtherButtons(decrement_button_
, increment_button_
);
638 fullscreen_button_
= new FullscreenButton(this);
639 // all buttons on menu should must be a custom button in order for
640 // the keyboard nativigation work.
641 DCHECK(CustomButton::AsCustomButton(fullscreen_button_
));
642 gfx::ImageSkia
* full_screen_image
=
643 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
644 IDR_FULLSCREEN_MENU_BUTTON
);
645 fullscreen_button_
->SetImage(ImageButton::STATE_NORMAL
, full_screen_image
);
646 SkColor fg_color
= native_theme
->GetSystemColor(
647 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
);
648 gfx::ImageSkia
hovered_fullscreen_image(
649 new HoveredImageSource(*full_screen_image
, fg_color
),
650 full_screen_image
->size());
651 fullscreen_button_
->SetImage(
652 ImageButton::STATE_HOVERED
, &hovered_fullscreen_image
);
653 fullscreen_button_
->SetImage(
654 ImageButton::STATE_PRESSED
, &hovered_fullscreen_image
);
656 SkColor enabled_text_color
= native_theme
->GetSystemColor(
657 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor
);
658 zoom_label_
->SetEnabledColor(enabled_text_color
);
659 decrement_button_
->SetTextColor(views::Button::STATE_NORMAL
,
661 increment_button_
->SetTextColor(views::Button::STATE_NORMAL
,
663 SkColor disabled_text_color
= native_theme
->GetSystemColor(
664 ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor
);
665 decrement_button_
->SetTextColor(views::Button::STATE_DISABLED
,
666 disabled_text_color
);
667 increment_button_
->SetTextColor(views::Button::STATE_DISABLED
,
668 disabled_text_color
);
669 fullscreen_button_
->SetFocusable(true);
670 fullscreen_button_
->set_request_focus_on_press(false);
671 fullscreen_button_
->set_tag(fullscreen_index
);
672 fullscreen_button_
->SetImageAlignment(
673 ImageButton::ALIGN_CENTER
, ImageButton::ALIGN_MIDDLE
);
674 int horizontal_padding
=
675 menu
->use_new_menu() ? kHorizontalTouchPadding
: kHorizontalPadding
;
676 fullscreen_button_
->SetBorder(views::Border::CreateEmptyBorder(
677 0, horizontal_padding
, 0, horizontal_padding
));
678 fullscreen_button_
->set_background(
679 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON
,
680 menu
->use_new_menu()));
681 fullscreen_button_
->SetAccessibleName(
682 GetAccessibleNameForWrenchMenuItem(
683 menu_model
, fullscreen_index
, IDS_ACCNAME_FULLSCREEN
));
684 AddChildView(fullscreen_button_
);
686 UpdateZoomControls();
689 virtual ~ZoomView() {}
691 // Overridden from View.
692 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
693 // The increment/decrement button are forced to the same width.
694 int button_width
= std::max(increment_button_
->GetPreferredSize().width(),
695 decrement_button_
->GetPreferredSize().width());
696 int zoom_padding
= use_new_menu() ?
697 kTouchZoomPadding
: kZoomPadding
;
698 int fullscreen_width
= fullscreen_button_
->GetPreferredSize().width() +
700 // Returned height doesn't matter as MenuItemView forces everything to the
701 // height of the menuitemview. Note that we have overridden the height when
702 // constructing the menu.
703 return gfx::Size(button_width
+ zoom_label_width_
+ button_width
+
704 fullscreen_width
, 0);
707 virtual void Layout() OVERRIDE
{
709 int button_width
= std::max(increment_button_
->GetPreferredSize().width(),
710 decrement_button_
->GetPreferredSize().width());
711 gfx::Rect
bounds(0, 0, button_width
, height());
713 decrement_button_
->SetBoundsRect(bounds
);
717 bounds
.set_width(zoom_label_width_
);
718 zoom_label_
->SetBoundsRect(bounds
);
722 bounds
.set_width(button_width
);
723 increment_button_
->SetBoundsRect(bounds
);
725 x
+= bounds
.width() + (use_new_menu() ? 0 : kZoomPadding
);
727 bounds
.set_width(fullscreen_button_
->GetPreferredSize().width() +
728 (use_new_menu() ? kTouchZoomPadding
: 0));
729 fullscreen_button_
->SetBoundsRect(bounds
);
732 // Overridden from ButtonListener.
733 virtual void ButtonPressed(views::Button
* sender
,
734 const ui::Event
& event
) OVERRIDE
{
735 if (sender
->tag() == fullscreen_index_
) {
736 menu()->CancelAndEvaluate(menu_model(), sender
->tag());
738 // Zoom buttons don't close the menu.
739 menu_model()->ActivatedAt(sender
->tag());
743 // Overridden from WrenchMenuObserver.
744 virtual void WrenchMenuDestroyed() OVERRIDE
{
745 WrenchMenuView::WrenchMenuDestroyed();
749 void OnZoomLevelChanged(const HostZoomMap::ZoomLevelChange
& change
) {
750 UpdateZoomControls();
753 void UpdateZoomControls() {
754 bool enable_increment
= false;
755 bool enable_decrement
= false;
756 WebContents
* selected_tab
=
757 menu()->browser_
->tab_strip_model()->GetActiveWebContents();
760 zoom
= selected_tab
->GetZoomPercent(&enable_increment
, &enable_decrement
);
761 increment_button_
->SetEnabled(enable_increment
);
762 decrement_button_
->SetEnabled(enable_decrement
);
763 zoom_label_
->SetText(
764 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, zoom
));
766 zoom_label_width_
= MaxWidthForZoomLabel();
769 // Calculates the max width the zoom string can be.
770 int MaxWidthForZoomLabel() {
771 const gfx::FontList
& font_list
= zoom_label_
->font_list();
773 zoom_label_
->border() ? zoom_label_
->border()->GetInsets().width() : 0;
777 WebContents
* selected_tab
=
778 menu()->browser_
->tab_strip_model()->GetActiveWebContents();
780 int min_percent
= selected_tab
->GetMinimumZoomPercent();
781 int max_percent
= selected_tab
->GetMaximumZoomPercent();
783 int step
= (max_percent
- min_percent
) / 10;
784 for (int i
= min_percent
; i
<= max_percent
; i
+= step
) {
785 int w
= gfx::GetStringWidth(
786 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, i
), font_list
);
787 max_w
= std::max(w
, max_w
);
790 max_w
= gfx::GetStringWidth(
791 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, 100), font_list
);
794 return max_w
+ border_width
;
797 // Index of the fullscreen menu item in the model.
798 const int fullscreen_index_
;
800 scoped_ptr
<content::HostZoomMap::Subscription
> zoom_subscription_
;
801 content::NotificationRegistrar registrar_
;
803 // Button for incrementing the zoom.
804 LabelButton
* increment_button_
;
806 // Label showing zoom as a percent.
809 // Button for decrementing the zoom.
810 LabelButton
* decrement_button_
;
812 ImageButton
* fullscreen_button_
;
814 // Width given to |zoom_label_|. This is the width at 100%.
815 int zoom_label_width_
;
817 DISALLOW_COPY_AND_ASSIGN(ZoomView
);
820 // RecentTabsMenuModelDelegate ------------------------------------------------
822 // Provides the ui::MenuModelDelegate implementation for RecentTabsSubMenuModel
824 class WrenchMenu::RecentTabsMenuModelDelegate
: public ui::MenuModelDelegate
{
826 RecentTabsMenuModelDelegate(WrenchMenu
* wrench_menu
,
827 ui::MenuModel
* model
,
828 views::MenuItemView
* menu_item
)
829 : wrench_menu_(wrench_menu
),
831 menu_item_(menu_item
) {
832 model_
->SetMenuModelDelegate(this);
835 virtual ~RecentTabsMenuModelDelegate() {
836 model_
->SetMenuModelDelegate(NULL
);
839 // Return the specific menu width of recent tabs submenu if |menu| is the
840 // recent tabs submenu, else return -1.
841 int GetMaxWidthForMenu(views::MenuItemView
* menu
) {
842 if (!menu_item_
->HasSubmenu())
844 const int kMaxMenuItemWidth
= 320;
845 return menu
->GetCommand() == menu_item_
->GetCommand() ?
846 kMaxMenuItemWidth
: -1;
849 const gfx::FontList
* GetLabelFontListAt(int index
) const {
850 return model_
->GetLabelFontListAt(index
);
853 bool GetShouldUseDisabledEmphasizedForegroundColor(int index
) const {
854 // The items for which we get a font list, should be shown in the bolded
856 return GetLabelFontListAt(index
) ? true : false;
859 // ui::MenuModelDelegate implementation:
861 virtual void OnIconChanged(int index
) OVERRIDE
{
862 int command_id
= model_
->GetCommandIdAt(index
);
863 views::MenuItemView
* item
= menu_item_
->GetMenuItemByID(command_id
);
866 model_
->GetIconAt(index
, &icon
);
867 item
->SetIcon(*icon
.ToImageSkia());
870 virtual void OnMenuStructureChanged() OVERRIDE
{
871 if (menu_item_
->HasSubmenu()) {
872 // Remove all menu items from submenu.
873 views::SubmenuView
* submenu
= menu_item_
->GetSubmenu();
874 while (submenu
->child_count() > 0)
875 menu_item_
->RemoveMenuItemAt(submenu
->child_count() - 1);
877 // Remove all elements in |WrenchMenu::command_id_to_entry_| that map to
879 WrenchMenu::CommandIDToEntry::iterator iter
=
880 wrench_menu_
->command_id_to_entry_
.begin();
881 while (iter
!= wrench_menu_
->command_id_to_entry_
.end()) {
882 if (iter
->second
.first
== model_
)
883 wrench_menu_
->command_id_to_entry_
.erase(iter
++);
889 // Add all menu items from |model| to submenu.
890 for (int i
= 0; i
< model_
->GetItemCount(); ++i
) {
891 wrench_menu_
->AddMenuItem(menu_item_
, i
, model_
, i
, model_
->GetTypeAt(i
),
895 // In case recent tabs submenu was open when items were changing, force a
896 // ChildrenChanged().
897 menu_item_
->ChildrenChanged();
901 WrenchMenu
* wrench_menu_
;
902 ui::MenuModel
* model_
;
903 views::MenuItemView
* menu_item_
;
905 DISALLOW_COPY_AND_ASSIGN(RecentTabsMenuModelDelegate
);
908 // WrenchMenu ------------------------------------------------------------------
910 WrenchMenu::WrenchMenu(Browser
* browser
,
912 bool supports_new_separators
)
915 selected_menu_model_(NULL
),
917 bookmark_menu_(NULL
),
918 feedback_menu_item_(NULL
),
919 use_new_menu_(use_new_menu
),
920 supports_new_separators_(supports_new_separators
) {
921 registrar_
.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
,
922 content::Source
<Profile
>(browser_
->profile()));
925 WrenchMenu::~WrenchMenu() {
926 if (bookmark_menu_delegate_
.get()) {
927 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(
928 browser_
->profile());
930 model
->RemoveObserver(this);
932 FOR_EACH_OBSERVER(WrenchMenuObserver
, observer_list_
, WrenchMenuDestroyed());
935 void WrenchMenu::Init(ui::MenuModel
* model
) {
937 root_
= new MenuItemView(this);
938 root_
->set_has_icons(true); // We have checks, radios and icons, set this
939 // so we get the taller menu style.
940 PopulateMenu(root_
, model
);
943 // Verify that the reserved command ID's for bookmarks menu are not used.
944 for (int i
= WrenchMenuModel
:kMinBookmarkCommandId
;
945 i
<= WrenchMenuModel::kMaxBookmarkCommandId
; ++i
)
946 DCHECK(command_id_to_entry_
.find(i
) == command_id_to_entry_
.end());
947 #endif // defined(DEBUG)
949 menu_runner_
.reset(new views::MenuRunner(root_
));
952 void WrenchMenu::RunMenu(views::MenuButton
* host
) {
953 gfx::Point screen_loc
;
954 views::View::ConvertPointToScreen(host
, &screen_loc
);
955 gfx::Rect
bounds(screen_loc
, host
->size());
956 content::RecordAction(UserMetricsAction("ShowAppMenu"));
957 if (menu_runner_
->RunMenuAt(host
->GetWidget(),
960 views::MENU_ANCHOR_TOPRIGHT
,
961 ui::MENU_SOURCE_NONE
,
962 views::MenuRunner::HAS_MNEMONICS
) ==
963 views::MenuRunner::MENU_DELETED
)
965 if (bookmark_menu_delegate_
.get()) {
966 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(
967 browser_
->profile());
969 model
->RemoveObserver(this);
971 if (selected_menu_model_
)
972 selected_menu_model_
->ActivatedAt(selected_index_
);
975 bool WrenchMenu::IsShowing() {
976 return menu_runner_
.get() && menu_runner_
->IsRunning();
979 const ui::NativeTheme
* WrenchMenu::GetNativeTheme() const {
980 views::Widget
* browser_widget
= views::Widget::GetWidgetForNativeView(
981 browser_
->window()->GetNativeWindow());
982 DCHECK(browser_widget
);
983 return browser_widget
->GetNativeTheme();
986 const views::MenuConfig
& WrenchMenu::GetMenuConfig() const {
987 return MenuConfig::instance(GetNativeTheme());
990 void WrenchMenu::AddObserver(WrenchMenuObserver
* observer
) {
991 observer_list_
.AddObserver(observer
);
994 void WrenchMenu::RemoveObserver(WrenchMenuObserver
* observer
) {
995 observer_list_
.RemoveObserver(observer
);
998 const gfx::FontList
* WrenchMenu::GetLabelFontList(int command_id
) const {
999 if (IsRecentTabsCommand(command_id
)) {
1000 return recent_tabs_menu_model_delegate_
->GetLabelFontListAt(
1001 ModelIndexFromCommandId(command_id
));
1006 bool WrenchMenu::GetShouldUseDisabledEmphasizedForegroundColor(
1007 int command_id
) const {
1008 if (IsRecentTabsCommand(command_id
)) {
1009 return recent_tabs_menu_model_delegate_
->
1010 GetShouldUseDisabledEmphasizedForegroundColor(
1011 ModelIndexFromCommandId(command_id
));
1016 base::string16
WrenchMenu::GetTooltipText(int command_id
,
1017 const gfx::Point
& p
) const {
1018 return IsBookmarkCommand(command_id
) ?
1019 bookmark_menu_delegate_
->GetTooltipText(command_id
, p
) : base::string16();
1022 bool WrenchMenu::IsTriggerableEvent(views::MenuItemView
* menu
,
1023 const ui::Event
& e
) {
1024 return IsBookmarkCommand(menu
->GetCommand()) ?
1025 bookmark_menu_delegate_
->IsTriggerableEvent(menu
, e
) :
1026 MenuDelegate::IsTriggerableEvent(menu
, e
);
1029 bool WrenchMenu::GetDropFormats(
1032 std::set
<ui::OSExchangeData::CustomFormat
>* custom_formats
) {
1033 CreateBookmarkMenu();
1034 return bookmark_menu_delegate_
.get() &&
1035 bookmark_menu_delegate_
->GetDropFormats(menu
, formats
, custom_formats
);
1038 bool WrenchMenu::AreDropTypesRequired(MenuItemView
* menu
) {
1039 CreateBookmarkMenu();
1040 return bookmark_menu_delegate_
.get() &&
1041 bookmark_menu_delegate_
->AreDropTypesRequired(menu
);
1044 bool WrenchMenu::CanDrop(MenuItemView
* menu
,
1045 const ui::OSExchangeData
& data
) {
1046 CreateBookmarkMenu();
1047 return bookmark_menu_delegate_
.get() &&
1048 bookmark_menu_delegate_
->CanDrop(menu
, data
);
1051 int WrenchMenu::GetDropOperation(
1053 const ui::DropTargetEvent
& event
,
1054 DropPosition
* position
) {
1055 return IsBookmarkCommand(item
->GetCommand()) ?
1056 bookmark_menu_delegate_
->GetDropOperation(item
, event
, position
) :
1057 ui::DragDropTypes::DRAG_NONE
;
1060 int WrenchMenu::OnPerformDrop(MenuItemView
* menu
,
1061 DropPosition position
,
1062 const ui::DropTargetEvent
& event
) {
1063 if (!IsBookmarkCommand(menu
->GetCommand()))
1064 return ui::DragDropTypes::DRAG_NONE
;
1066 int result
= bookmark_menu_delegate_
->OnPerformDrop(menu
, position
, event
);
1070 bool WrenchMenu::ShowContextMenu(MenuItemView
* source
,
1072 const gfx::Point
& p
,
1073 ui::MenuSourceType source_type
) {
1074 return IsBookmarkCommand(command_id
) ?
1075 bookmark_menu_delegate_
->ShowContextMenu(source
, command_id
, p
,
1080 bool WrenchMenu::CanDrag(MenuItemView
* menu
) {
1081 return IsBookmarkCommand(menu
->GetCommand()) ?
1082 bookmark_menu_delegate_
->CanDrag(menu
) : false;
1085 void WrenchMenu::WriteDragData(MenuItemView
* sender
,
1086 ui::OSExchangeData
* data
) {
1087 DCHECK(IsBookmarkCommand(sender
->GetCommand()));
1088 return bookmark_menu_delegate_
->WriteDragData(sender
, data
);
1091 int WrenchMenu::GetDragOperations(MenuItemView
* sender
) {
1092 return IsBookmarkCommand(sender
->GetCommand()) ?
1093 bookmark_menu_delegate_
->GetDragOperations(sender
) :
1094 MenuDelegate::GetDragOperations(sender
);
1097 int WrenchMenu::GetMaxWidthForMenu(MenuItemView
* menu
) {
1098 if (IsBookmarkCommand(menu
->GetCommand()))
1099 return bookmark_menu_delegate_
->GetMaxWidthForMenu(menu
);
1101 // If recent tabs menu is available, it will decide if |menu| is one of recent
1102 // tabs; if yes, it would return the menu width for recent tabs.
1103 // otherwise, it would return -1.
1104 if (recent_tabs_menu_model_delegate_
.get())
1105 max_width
= recent_tabs_menu_model_delegate_
->GetMaxWidthForMenu(menu
);
1106 if (max_width
== -1)
1107 max_width
= MenuDelegate::GetMaxWidthForMenu(menu
);
1111 bool WrenchMenu::IsItemChecked(int command_id
) const {
1112 if (IsBookmarkCommand(command_id
))
1115 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1116 return entry
.first
->IsItemCheckedAt(entry
.second
);
1119 bool WrenchMenu::IsCommandEnabled(int command_id
) const {
1120 if (IsBookmarkCommand(command_id
))
1123 if (command_id
== 0)
1124 return false; // The root item.
1126 // The items representing the cut menu (cut/copy/paste) and zoom menu
1127 // (increment/decrement/reset) are always enabled. The child views of these
1128 // items enabled state updates appropriately.
1129 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
)
1132 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1133 return entry
.first
->IsEnabledAt(entry
.second
);
1136 void WrenchMenu::ExecuteCommand(int command_id
, int mouse_event_flags
) {
1137 if (IsBookmarkCommand(command_id
)) {
1138 bookmark_menu_delegate_
->ExecuteCommand(command_id
, mouse_event_flags
);
1142 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
) {
1143 // These items are represented by child views. If ExecuteCommand is invoked
1144 // it means the user clicked on the area around the buttons and we should
1149 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1150 return entry
.first
->ActivatedAt(entry
.second
, mouse_event_flags
);
1153 bool WrenchMenu::GetAccelerator(int command_id
,
1154 ui::Accelerator
* accelerator
) const {
1155 if (IsBookmarkCommand(command_id
))
1158 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
) {
1159 // These have special child views; don't show the accelerator for them.
1163 CommandIDToEntry::const_iterator ix
= command_id_to_entry_
.find(command_id
);
1164 const Entry
& entry
= ix
->second
;
1165 ui::Accelerator menu_accelerator
;
1166 if (!entry
.first
->GetAcceleratorAt(entry
.second
, &menu_accelerator
))
1169 *accelerator
= ui::Accelerator(menu_accelerator
.key_code(),
1170 menu_accelerator
.modifiers());
1174 void WrenchMenu::WillShowMenu(MenuItemView
* menu
) {
1175 if (menu
== bookmark_menu_
)
1176 CreateBookmarkMenu();
1179 void WrenchMenu::WillHideMenu(MenuItemView
* menu
) {
1180 // Turns off the fade out animation of the wrench menus if
1181 // |feedback_menu_item_| is selected. This excludes the wrench menu itself
1182 // from the snapshot in the feedback UI.
1183 if (menu
->HasSubmenu() && feedback_menu_item_
&&
1184 feedback_menu_item_
->IsSelected()) {
1185 // It's okay to just turn off the animation and no to take care the
1186 // animation back because the menu widget will be recreated next time
1187 // it's opened. See ToolbarView::RunMenu() and Init() of this class.
1188 menu
->GetSubmenu()->GetWidget()->
1189 SetVisibilityChangedAnimationsEnabled(false);
1193 void WrenchMenu::BookmarkModelChanged() {
1194 DCHECK(bookmark_menu_delegate_
.get());
1195 if (!bookmark_menu_delegate_
->is_mutating_model())
1199 void WrenchMenu::Observe(int type
,
1200 const content::NotificationSource
& source
,
1201 const content::NotificationDetails
& details
) {
1203 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
:
1204 // A change in the global errors list can add or remove items from the
1205 // menu. Close the menu to avoid have a stale menu on-screen.
1214 void WrenchMenu::PopulateMenu(MenuItemView
* parent
,
1216 for (int i
= 0, max
= model
->GetItemCount(); i
< max
; ++i
) {
1217 // The button container menu items have a special height which we have to
1218 // use instead of the normal height.
1220 if (use_new_menu_
&&
1221 (model
->GetCommandIdAt(i
) == IDC_CUT
||
1222 model
->GetCommandIdAt(i
) == IDC_ZOOM_MINUS
))
1223 height
= kMenuItemContainingButtonsHeight
;
1225 // Add the menu item at the end.
1226 int menu_index
= parent
->HasSubmenu() ?
1227 parent
->GetSubmenu()->child_count() : 0;
1228 MenuItemView
* item
= AddMenuItem(
1229 parent
, menu_index
, model
, i
, model
->GetTypeAt(i
), height
);
1231 if (model
->GetTypeAt(i
) == MenuModel::TYPE_SUBMENU
)
1232 PopulateMenu(item
, model
->GetSubmenuModelAt(i
));
1234 const ui::NativeTheme
* native_theme
= GetNativeTheme();
1236 switch (model
->GetCommandIdAt(i
)) {
1238 DCHECK_EQ(MenuModel::TYPE_COMMAND
, model
->GetTypeAt(i
));
1239 DCHECK_LT(i
+ 2, max
);
1240 DCHECK_EQ(IDC_COPY
, model
->GetCommandIdAt(i
+ 1));
1241 DCHECK_EQ(IDC_PASTE
, model
->GetCommandIdAt(i
+ 2));
1242 item
->SetTitle(l10n_util::GetStringUTF16(IDS_EDIT2
));
1243 item
->AddChildView(new CutCopyPasteView(this, model
, native_theme
,
1248 case IDC_ZOOM_MINUS
:
1249 DCHECK_EQ(MenuModel::TYPE_COMMAND
, model
->GetTypeAt(i
));
1250 DCHECK_EQ(IDC_ZOOM_PLUS
, model
->GetCommandIdAt(i
+ 1));
1251 DCHECK_EQ(IDC_FULLSCREEN
, model
->GetCommandIdAt(i
+ 2));
1252 item
->SetTitle(l10n_util::GetStringUTF16(IDS_ZOOM_MENU2
));
1253 item
->AddChildView(new ZoomView(this, model
, native_theme
,
1258 case IDC_BOOKMARKS_MENU
:
1259 DCHECK(!bookmark_menu_
);
1260 bookmark_menu_
= item
;
1263 #if defined(GOOGLE_CHROME_BUILD)
1265 DCHECK(!feedback_menu_item_
);
1266 feedback_menu_item_
= item
;
1270 case IDC_RECENT_TABS_MENU
:
1271 DCHECK(!recent_tabs_menu_model_delegate_
.get());
1272 recent_tabs_menu_model_delegate_
.reset(
1273 new RecentTabsMenuModelDelegate(this, model
->GetSubmenuModelAt(i
),
1283 MenuItemView
* WrenchMenu::AddMenuItem(MenuItemView
* parent
,
1287 MenuModel::ItemType menu_type
,
1289 int command_id
= model
->GetCommandIdAt(model_index
);
1290 DCHECK(command_id
> -1 ||
1291 (command_id
== -1 &&
1292 model
->GetTypeAt(model_index
) == MenuModel::TYPE_SEPARATOR
));
1294 if (command_id
> -1) { // Don't add separators to |command_id_to_entry_|.
1295 // All command ID's should be unique except for IDC_SHOW_HISTORY which is
1296 // in both wrench menu and RecentTabs submenu,
1297 if (command_id
!= IDC_SHOW_HISTORY
) {
1298 DCHECK(command_id_to_entry_
.find(command_id
) ==
1299 command_id_to_entry_
.end())
1300 << "command ID " << command_id
<< " already exists!";
1302 command_id_to_entry_
[command_id
].first
= model
;
1303 command_id_to_entry_
[command_id
].second
= model_index
;
1306 MenuItemView
* menu_item
= NULL
;
1308 // For menu items with a special menu height we use our special class to be
1309 // able to modify the item height.
1310 menu_item
= new ButtonContainerMenuItemView(parent
, command_id
, height
);
1311 parent
->GetSubmenu()->AddChildViewAt(menu_item
, menu_index
);
1313 // For all other cases we use the more generic way to add menu items.
1314 menu_item
= views::MenuModelAdapter::AddMenuItemFromModelAt(
1315 model
, model_index
, parent
, menu_index
, command_id
);
1319 // Flush all buttons to the right side of the menu for the new menu type.
1320 menu_item
->set_use_right_margin(!use_new_menu_
);
1321 menu_item
->SetVisible(model
->IsVisibleAt(model_index
));
1323 if (menu_type
== MenuModel::TYPE_COMMAND
&& model
->HasIcons()) {
1325 if (model
->GetIconAt(model_index
, &icon
))
1326 menu_item
->SetIcon(*icon
.ToImageSkia());
1333 void WrenchMenu::CancelAndEvaluate(MenuModel
* model
, int index
) {
1334 selected_menu_model_
= model
;
1335 selected_index_
= index
;
1339 void WrenchMenu::CreateBookmarkMenu() {
1340 if (bookmark_menu_delegate_
.get())
1341 return; // Already created the menu.
1343 BookmarkModel
* model
=
1344 BookmarkModelFactory::GetForProfile(browser_
->profile());
1345 if (!model
->loaded())
1348 model
->AddObserver(this);
1350 // TODO(oshima): Replace with views only API.
1351 views::Widget
* parent
= views::Widget::GetWidgetForNativeWindow(
1352 browser_
->window()->GetNativeWindow());
1353 bookmark_menu_delegate_
.reset(
1354 new BookmarkMenuDelegate(browser_
,
1357 WrenchMenuModel::kMinBookmarkCommandId
,
1358 WrenchMenuModel::kMaxBookmarkCommandId
));
1359 bookmark_menu_delegate_
->Init(this,
1361 model
->bookmark_bar_node(),
1363 BookmarkMenuDelegate::SHOW_PERMANENT_FOLDERS
,
1364 BOOKMARK_LAUNCH_LOCATION_WRENCH_MENU
);
1367 int WrenchMenu::ModelIndexFromCommandId(int command_id
) const {
1368 CommandIDToEntry::const_iterator ix
= command_id_to_entry_
.find(command_id
);
1369 DCHECK(ix
!= command_id_to_entry_
.end());
1370 return ix
->second
.second
;