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.h"
15 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
16 #include "chrome/browser/bookmarks/bookmark_stats.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/search/search.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
24 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
25 #include "chrome/browser/ui/views/toolbar/wrench_menu_observer.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"
61 #include "ui/native_theme/native_theme_aura.h"
64 using base::UserMetricsAction
;
65 using content::HostZoomMap
;
66 using content::WebContents
;
68 using views::CustomButton
;
69 using views::ImageButton
;
71 using views::LabelButton
;
72 using views::MenuConfig
;
73 using views::MenuItemView
;
78 // Horizontal padding on the edges of the buttons.
79 const int kHorizontalPadding
= 6;
80 // Horizontal padding for a touch enabled menu.
81 const int kHorizontalTouchPadding
= 15;
83 // Menu items which have embedded buttons should have this height in pixel.
84 const int kMenuItemContainingButtonsHeight
= 43;
86 // Returns true if |command_id| identifies a bookmark menu item.
87 bool IsBookmarkCommand(int command_id
) {
88 return command_id
>= WrenchMenuModel::kMinBookmarkCommandId
&&
89 command_id
<= WrenchMenuModel::kMaxBookmarkCommandId
;
92 // Returns true if |command_id| identifies a recent tabs menu item.
93 bool IsRecentTabsCommand(int command_id
) {
94 return command_id
>= WrenchMenuModel::kMinRecentTabsCommandId
&&
95 command_id
<= WrenchMenuModel::kMaxRecentTabsCommandId
;
98 // Subclass of ImageButton whose preferred size includes the size of the border.
99 class FullscreenButton
: public ImageButton
{
101 explicit FullscreenButton(views::ButtonListener
* listener
)
102 : ImageButton(listener
) { }
104 // Overridden from ImageButton.
105 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
106 gfx::Size pref
= ImageButton::GetPreferredSize();
108 gfx::Insets insets
= border()->GetInsets();
109 pref
.Enlarge(insets
.width(), insets
.height());
115 DISALLOW_COPY_AND_ASSIGN(FullscreenButton
);
118 // Border for buttons contained in the menu. This is only used for getting the
119 // insets, the actual painting is done in MenuButtonBackground.
120 class MenuButtonBorder
: public views::Border
{
122 MenuButtonBorder(const MenuConfig
& config
, bool use_new_menu
)
123 : horizontal_padding_(use_new_menu
?
124 kHorizontalTouchPadding
: kHorizontalPadding
),
125 insets_(config
.item_top_margin
, horizontal_padding_
,
126 config
.item_bottom_margin
, horizontal_padding_
) {
129 // Overridden from views::Border.
130 virtual void Paint(const View
& view
, gfx::Canvas
* canvas
) OVERRIDE
{
131 // Painting of border is done in MenuButtonBackground.
134 virtual gfx::Insets
GetInsets() const OVERRIDE
{
138 virtual gfx::Size
GetMinimumSize() const OVERRIDE
{
139 // This size is sufficient for MenuButtonBackground::Paint() to draw any of
141 return gfx::Size(4, 4);
145 // The horizontal padding dependent on the layout.
146 const int horizontal_padding_
;
148 const gfx::Insets insets_
;
150 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder
);
153 // Combination border/background for the buttons contained in the menu. The
154 // painting of the border/background is done here as TextButton does not always
156 class MenuButtonBackground
: public views::Background
{
165 MenuButtonBackground(ButtonType type
, bool use_new_menu
)
167 use_new_menu_(use_new_menu
),
169 right_button_(NULL
) {}
171 // Used when the type is CENTER_BUTTON to determine if the left/right edge
172 // needs to be rendered selected.
173 void SetOtherButtons(CustomButton
* left_button
, CustomButton
* right_button
) {
174 if (base::i18n::IsRTL()) {
175 left_button_
= right_button
;
176 right_button_
= left_button
;
178 left_button_
= left_button
;
179 right_button_
= right_button
;
183 // Overridden from views::Background.
184 virtual void Paint(gfx::Canvas
* canvas
, View
* view
) const OVERRIDE
{
185 CustomButton
* button
= CustomButton::AsCustomButton(view
);
186 views::Button::ButtonState state
=
187 button
? button
->state() : views::Button::STATE_NORMAL
;
188 int w
= view
->width();
189 int h
= view
->height();
190 #if defined(USE_AURA)
191 // Normal buttons get a border drawn on the right side and the rest gets
192 // filled in. The left button however does not get a line to combine
195 if (type_
!= RIGHT_BUTTON
) {
197 canvas
->FillRect(gfx::Rect(0, 0, border
, h
),
198 BorderColor(view
, views::Button::STATE_NORMAL
));
201 gfx::Rect
bounds(view
->GetLocalBounds());
202 bounds
.set_x(view
->GetMirroredXForRect(bounds
));
203 DrawBackground(canvas
, view
, bounds
, state
);
207 const SkColor border_color
= BorderColor(view
, state
);
208 switch (TypeAdjustedForRTL()) {
209 // TODO(pkasting): Why don't all the following use SkPaths with rounded
212 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
, h
- 2), state
);
213 canvas
->FillRect(gfx::Rect(2, 0, w
, 1), border_color
);
214 canvas
->FillRect(gfx::Rect(1, 1, 1, 1), border_color
);
215 canvas
->FillRect(gfx::Rect(0, 2, 1, h
- 4), border_color
);
216 canvas
->FillRect(gfx::Rect(1, h
- 2, 1, 1), border_color
);
217 canvas
->FillRect(gfx::Rect(2, h
- 1, w
, 1), border_color
);
220 case CENTER_BUTTON
: {
221 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
- 2, h
- 2), state
);
222 SkColor left_color
= state
!= views::Button::STATE_NORMAL
?
223 border_color
: BorderColor(view
, left_button_
->state());
224 canvas
->FillRect(gfx::Rect(0, 0, 1, h
), left_color
);
225 canvas
->FillRect(gfx::Rect(1, 0, w
- 2, 1), border_color
);
226 canvas
->FillRect(gfx::Rect(1, h
- 1, w
- 2, 1),
228 SkColor right_color
= state
!= views::Button::STATE_NORMAL
?
229 border_color
: BorderColor(view
, right_button_
->state());
230 canvas
->FillRect(gfx::Rect(w
- 1, 0, 1, h
), right_color
);
235 DrawBackground(canvas
, view
, gfx::Rect(0, 1, w
- 1, h
- 2), state
);
236 canvas
->FillRect(gfx::Rect(0, 0, w
- 2, 1), border_color
);
237 canvas
->FillRect(gfx::Rect(w
- 2, 1, 1, 1), border_color
);
238 canvas
->FillRect(gfx::Rect(w
- 1, 2, 1, h
- 4), border_color
);
239 canvas
->FillRect(gfx::Rect(w
- 2, h
- 2, 1, 1), border_color
);
240 canvas
->FillRect(gfx::Rect(0, h
- 1, w
- 2, 1), border_color
);
244 DrawBackground(canvas
, view
, gfx::Rect(1, 1, w
- 2, h
- 2), state
);
245 canvas
->FillRect(gfx::Rect(2, 0, w
- 4, 1), border_color
);
246 canvas
->FillRect(gfx::Rect(1, 1, 1, 1), border_color
);
247 canvas
->FillRect(gfx::Rect(0, 2, 1, h
- 4), border_color
);
248 canvas
->FillRect(gfx::Rect(1, h
- 2, 1, 1), border_color
);
249 canvas
->FillRect(gfx::Rect(2, h
- 1, w
- 4, 1), border_color
);
250 canvas
->FillRect(gfx::Rect(w
- 2, 1, 1, 1), border_color
);
251 canvas
->FillRect(gfx::Rect(w
- 1, 2, 1, h
- 4), border_color
);
252 canvas
->FillRect(gfx::Rect(w
- 2, h
- 2, 1, 1), border_color
);
262 static SkColor
BorderColor(View
* view
, views::Button::ButtonState state
) {
263 ui::NativeTheme
* theme
= view
->GetNativeTheme();
265 case views::Button::STATE_HOVERED
:
266 return theme
->GetSystemColor(
267 ui::NativeTheme::kColorId_HoverMenuButtonBorderColor
);
268 case views::Button::STATE_PRESSED
:
269 return theme
->GetSystemColor(
270 ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor
);
272 return theme
->GetSystemColor(
273 ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor
);
277 static SkColor
BackgroundColor(const View
* view
,
278 views::Button::ButtonState state
) {
279 const ui::NativeTheme
* theme
= view
->GetNativeTheme();
281 case views::Button::STATE_HOVERED
:
282 // Hovered should be handled in DrawBackground.
284 return theme
->GetSystemColor(
285 ui::NativeTheme::kColorId_HoverMenuItemBackgroundColor
);
286 case views::Button::STATE_PRESSED
:
287 return theme
->GetSystemColor(
288 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor
);
290 return theme
->GetSystemColor(
291 ui::NativeTheme::kColorId_MenuBackgroundColor
);
295 void DrawBackground(gfx::Canvas
* canvas
,
296 const views::View
* view
,
297 const gfx::Rect
& bounds
,
298 views::Button::ButtonState state
) const {
299 if (state
== views::Button::STATE_HOVERED
||
300 state
== views::Button::STATE_PRESSED
) {
301 view
->GetNativeTheme()->Paint(canvas
->sk_canvas(),
302 ui::NativeTheme::kMenuItemBackground
,
303 ui::NativeTheme::kHovered
,
305 ui::NativeTheme::ExtraParams());
310 canvas
->FillRect(bounds
, BackgroundColor(view
, state
));
313 ButtonType
TypeAdjustedForRTL() const {
314 if (!base::i18n::IsRTL())
318 case LEFT_BUTTON
: return RIGHT_BUTTON
;
319 case RIGHT_BUTTON
: return LEFT_BUTTON
;
325 const ButtonType type_
;
326 const bool use_new_menu_
;
328 // See description above setter for details.
329 CustomButton
* left_button_
;
330 CustomButton
* right_button_
;
332 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground
);
335 base::string16
GetAccessibleNameForWrenchMenuItem(
336 MenuModel
* model
, int item_index
, int accessible_string_id
) {
337 base::string16 accessible_name
=
338 l10n_util::GetStringUTF16(accessible_string_id
);
339 base::string16 accelerator_text
;
341 ui::Accelerator menu_accelerator
;
342 if (model
->GetAcceleratorAt(item_index
, &menu_accelerator
)) {
344 ui::Accelerator(menu_accelerator
.key_code(),
345 menu_accelerator
.modifiers()).GetShortcutText();
348 return MenuItemView::GetAccessibleNameForMenuItem(
349 accessible_name
, accelerator_text
);
352 // WrenchMenuView is a view that can contain label buttons.
353 class WrenchMenuView
: public views::View
,
354 public views::ButtonListener
,
355 public WrenchMenuObserver
{
357 WrenchMenuView(WrenchMenu
* menu
, MenuModel
* menu_model
)
359 menu_model_(menu_model
) {
360 menu_
->AddObserver(this);
363 virtual ~WrenchMenuView() {
365 menu_
->RemoveObserver(this);
368 // Overridden from views::View.
369 virtual void SchedulePaintInRect(const gfx::Rect
& r
) OVERRIDE
{
370 // Normally when the mouse enters/exits a button the buttons invokes
371 // SchedulePaint. As part of the button border (MenuButtonBackground) is
372 // rendered by the button to the left/right of it SchedulePaint on the the
373 // button may not be enough, so this forces a paint all.
374 View::SchedulePaintInRect(gfx::Rect(size()));
377 LabelButton
* CreateAndConfigureButton(int string_id
,
378 MenuButtonBackground::ButtonType type
,
380 MenuButtonBackground
** background
) {
381 return CreateButtonWithAccName(
382 string_id
, type
, index
, background
, string_id
);
385 LabelButton
* CreateButtonWithAccName(int string_id
,
386 MenuButtonBackground::ButtonType type
,
388 MenuButtonBackground
** background
,
390 // Should only be invoked during construction when |menu_| is valid.
392 LabelButton
* button
= new LabelButton(this, gfx::RemoveAcceleratorChar(
393 l10n_util::GetStringUTF16(string_id
), '&', NULL
, NULL
));
394 button
->SetAccessibleName(
395 GetAccessibleNameForWrenchMenuItem(menu_model_
, index
, acc_string_id
));
396 button
->SetFocusable(true);
397 button
->set_request_focus_on_press(false);
398 button
->set_tag(index
);
399 button
->SetEnabled(menu_model_
->IsEnabledAt(index
));
400 MenuButtonBackground
* bg
=
401 new MenuButtonBackground(type
, menu_
->use_new_menu());
402 button
->set_background(bg
);
403 const MenuConfig
& menu_config
= menu_
->GetMenuConfig();
404 button
->SetTextColor(views::Button::STATE_NORMAL
, menu_config
.text_color
);
408 new MenuButtonBorder(menu_config
, menu_
->use_new_menu()));
409 button
->SetHorizontalAlignment(gfx::ALIGN_CENTER
);
410 button
->SetFontList(menu_config
.font_list
);
411 ui::NativeTheme
* native_theme
= button
->GetNativeTheme();
412 button
->SetTextColor(
413 views::Button::STATE_DISABLED
,
414 native_theme
->GetSystemColor(
415 ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor
));
416 button
->SetTextColor(
417 views::Button::STATE_HOVERED
,
418 native_theme
->GetSystemColor(
419 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
));
420 button
->SetTextColor(
421 views::Button::STATE_PRESSED
,
422 native_theme
->GetSystemColor(
423 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
));
424 button
->SetTextColor(
425 views::Button::STATE_NORMAL
,
426 native_theme
->GetSystemColor(
427 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor
));
428 AddChildView(button
);
429 // all buttons on menu should must be a custom button in order for
430 // the keyboard nativigation work.
431 DCHECK(CustomButton::AsCustomButton(button
));
435 // Overridden from WrenchMenuObserver:
436 virtual void WrenchMenuDestroyed() OVERRIDE
{
437 menu_
->RemoveObserver(this);
443 WrenchMenu
* menu() { return menu_
; }
444 MenuModel
* menu_model() { return menu_model_
; }
447 // Hosting WrenchMenu.
448 // WARNING: this may be NULL during shutdown.
451 // The menu model containing the increment/decrement/reset items.
452 // WARNING: this may be NULL during shutdown.
453 MenuModel
* menu_model_
;
455 DISALLOW_COPY_AND_ASSIGN(WrenchMenuView
);
458 class ButtonContainerMenuItemView
: public MenuItemView
{
460 // Constructor for use with button containing menu items which have a
461 // different height then normal items.
462 ButtonContainerMenuItemView(MenuItemView
* parent
, int command_id
, int height
)
463 : MenuItemView(parent
, command_id
, MenuItemView::NORMAL
),
467 // Overridden from MenuItemView.
468 virtual gfx::Size
GetChildPreferredSize() OVERRIDE
{
469 gfx::Size size
= MenuItemView::GetChildPreferredSize();
470 // When there is a height override given, we need to deduct our spacing
471 // above and below to get to the correct height to return here for the
473 int height
= height_
- GetTopMargin() - GetBottomMargin();
474 if (height
> size
.height())
475 size
.set_height(height
);
482 DISALLOW_COPY_AND_ASSIGN(ButtonContainerMenuItemView
);
485 // Generate the button image for hover state.
486 class HoveredImageSource
: public gfx::ImageSkiaSource
{
488 HoveredImageSource(const gfx::ImageSkia
& image
, SkColor color
)
492 virtual ~HoveredImageSource() {}
494 virtual gfx::ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
495 const gfx::ImageSkiaRep
& rep
= image_
.GetRepresentation(scale
);
496 SkBitmap bitmap
= rep
.sk_bitmap();
498 white
.setConfig(SkBitmap::kARGB_8888_Config
,
499 bitmap
.width(), bitmap
.height(), 0);
501 white
.eraseARGB(0, 0, 0, 0);
503 for (int y
= 0; y
< bitmap
.height(); ++y
) {
504 uint32
* image_row
= bitmap
.getAddr32(0, y
);
505 uint32
* dst_row
= white
.getAddr32(0, y
);
506 for (int x
= 0; x
< bitmap
.width(); ++x
) {
507 uint32 image_pixel
= image_row
[x
];
508 // Fill the non transparent pixels with |color_|.
509 dst_row
[x
] = (image_pixel
& 0xFF000000) == 0x0 ? 0x0 : color_
;
512 bitmap
.unlockPixels();
513 return gfx::ImageSkiaRep(white
, scale
);
517 const gfx::ImageSkia image_
;
518 const SkColor color_
;
519 DISALLOW_COPY_AND_ASSIGN(HoveredImageSource
);
524 // CutCopyPasteView ------------------------------------------------------------
526 // CutCopyPasteView is the view containing the cut/copy/paste buttons.
527 class WrenchMenu::CutCopyPasteView
: public WrenchMenuView
{
529 CutCopyPasteView(WrenchMenu
* menu
,
530 MenuModel
* menu_model
,
531 const ui::NativeTheme
* native_theme
,
535 : WrenchMenuView(menu
, menu_model
) {
536 LabelButton
* cut
= CreateAndConfigureButton(
537 IDS_CUT
, MenuButtonBackground::LEFT_BUTTON
, cut_index
, NULL
);
538 MenuButtonBackground
* copy_background
= NULL
;
539 CreateAndConfigureButton(
540 IDS_COPY
, MenuButtonBackground::CENTER_BUTTON
, copy_index
,
542 LabelButton
* paste
= CreateAndConfigureButton(
544 menu
->use_new_menu() && menu
->supports_new_separators_
?
545 MenuButtonBackground::CENTER_BUTTON
:
546 MenuButtonBackground::RIGHT_BUTTON
,
549 copy_background
->SetOtherButtons(cut
, paste
);
552 // Overridden from View.
553 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
554 // Returned height doesn't matter as MenuItemView forces everything to the
555 // height of the menuitemview.
556 return gfx::Size(GetMaxChildViewPreferredWidth() * child_count(), 0);
559 virtual void Layout() OVERRIDE
{
560 // All buttons are given the same width.
561 int width
= GetMaxChildViewPreferredWidth();
562 for (int i
= 0; i
< child_count(); ++i
)
563 child_at(i
)->SetBounds(i
* width
, 0, width
, height());
566 // Overridden from ButtonListener.
567 virtual void ButtonPressed(views::Button
* sender
,
568 const ui::Event
& event
) OVERRIDE
{
569 menu()->CancelAndEvaluate(menu_model(), sender
->tag());
573 // Returns the max preferred width of all the children.
574 int GetMaxChildViewPreferredWidth() {
576 for (int i
= 0; i
< child_count(); ++i
)
577 width
= std::max(width
, child_at(i
)->GetPreferredSize().width());
581 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView
);
584 // ZoomView --------------------------------------------------------------------
586 // Padding between the increment buttons and the reset button.
587 static const int kZoomPadding
= 6;
588 static const int kTouchZoomPadding
= 14;
590 // ZoomView contains the various zoom controls: two buttons to increase/decrease
591 // the zoom, a label showing the current zoom percent, and a button to go
593 class WrenchMenu::ZoomView
: public WrenchMenuView
{
595 ZoomView(WrenchMenu
* menu
,
596 MenuModel
* menu_model
,
597 const ui::NativeTheme
* native_theme
,
600 int fullscreen_index
)
601 : WrenchMenuView(menu
, menu_model
),
602 fullscreen_index_(fullscreen_index
),
603 increment_button_(NULL
),
605 decrement_button_(NULL
),
606 fullscreen_button_(NULL
),
607 zoom_label_width_(0) {
608 zoom_subscription_
= HostZoomMap::GetForBrowserContext(
609 menu
->browser_
->profile())->AddZoomLevelChangedCallback(
610 base::Bind(&WrenchMenu::ZoomView::OnZoomLevelChanged
,
611 base::Unretained(this)));
613 decrement_button_
= CreateButtonWithAccName(
614 IDS_ZOOM_MINUS2
, MenuButtonBackground::LEFT_BUTTON
, decrement_index
,
615 NULL
, IDS_ACCNAME_ZOOM_MINUS2
);
617 zoom_label_
= new Label(
618 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, 100));
619 zoom_label_
->SetAutoColorReadabilityEnabled(false);
620 zoom_label_
->SetHorizontalAlignment(gfx::ALIGN_RIGHT
);
622 MenuButtonBackground
* center_bg
= new MenuButtonBackground(
623 menu
->use_new_menu() && menu
->supports_new_separators_
?
624 MenuButtonBackground::RIGHT_BUTTON
:
625 MenuButtonBackground::CENTER_BUTTON
,
626 menu
->use_new_menu());
627 zoom_label_
->set_background(center_bg
);
628 const MenuConfig
& menu_config(menu
->GetMenuConfig());
629 zoom_label_
->set_border(
630 new MenuButtonBorder(menu_config
, menu
->use_new_menu()));
631 zoom_label_
->SetFontList(menu_config
.font_list
);
633 AddChildView(zoom_label_
);
634 zoom_label_width_
= MaxWidthForZoomLabel();
636 increment_button_
= CreateButtonWithAccName(
637 IDS_ZOOM_PLUS2
, MenuButtonBackground::RIGHT_BUTTON
, increment_index
,
638 NULL
, IDS_ACCNAME_ZOOM_PLUS2
);
640 center_bg
->SetOtherButtons(decrement_button_
, increment_button_
);
642 fullscreen_button_
= new FullscreenButton(this);
643 // all buttons on menu should must be a custom button in order for
644 // the keyboard nativigation work.
645 DCHECK(CustomButton::AsCustomButton(fullscreen_button_
));
646 gfx::ImageSkia
* full_screen_image
=
647 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
648 IDR_FULLSCREEN_MENU_BUTTON
);
649 fullscreen_button_
->SetImage(ImageButton::STATE_NORMAL
, full_screen_image
);
650 SkColor fg_color
= native_theme
->GetSystemColor(
651 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor
);
652 gfx::ImageSkia
hovered_fullscreen_image(
653 new HoveredImageSource(*full_screen_image
, fg_color
),
654 full_screen_image
->size());
655 fullscreen_button_
->SetImage(
656 ImageButton::STATE_HOVERED
, &hovered_fullscreen_image
);
657 fullscreen_button_
->SetImage(
658 ImageButton::STATE_PRESSED
, &hovered_fullscreen_image
);
660 SkColor enabled_text_color
= native_theme
->GetSystemColor(
661 ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor
);
662 zoom_label_
->SetEnabledColor(enabled_text_color
);
663 decrement_button_
->SetTextColor(views::Button::STATE_NORMAL
,
665 increment_button_
->SetTextColor(views::Button::STATE_NORMAL
,
667 SkColor disabled_text_color
= native_theme
->GetSystemColor(
668 ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor
);
669 decrement_button_
->SetTextColor(views::Button::STATE_DISABLED
,
670 disabled_text_color
);
671 increment_button_
->SetTextColor(views::Button::STATE_DISABLED
,
672 disabled_text_color
);
673 fullscreen_button_
->SetFocusable(true);
674 fullscreen_button_
->set_request_focus_on_press(false);
675 fullscreen_button_
->set_tag(fullscreen_index
);
676 fullscreen_button_
->SetImageAlignment(
677 ImageButton::ALIGN_CENTER
, ImageButton::ALIGN_MIDDLE
);
678 int horizontal_padding
=
679 menu
->use_new_menu() ? kHorizontalTouchPadding
: kHorizontalPadding
;
680 fullscreen_button_
->set_border(views::Border::CreateEmptyBorder(
681 0, horizontal_padding
, 0, horizontal_padding
));
682 fullscreen_button_
->set_background(
683 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON
,
684 menu
->use_new_menu()));
685 fullscreen_button_
->SetAccessibleName(
686 GetAccessibleNameForWrenchMenuItem(
687 menu_model
, fullscreen_index
, IDS_ACCNAME_FULLSCREEN
));
688 AddChildView(fullscreen_button_
);
690 UpdateZoomControls();
693 virtual ~ZoomView() {}
695 // Overridden from View.
696 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
697 // The increment/decrement button are forced to the same width.
698 int button_width
= std::max(increment_button_
->GetPreferredSize().width(),
699 decrement_button_
->GetPreferredSize().width());
700 int zoom_padding
= menu()->use_new_menu() ?
701 kTouchZoomPadding
: kZoomPadding
;
702 int fullscreen_width
= fullscreen_button_
->GetPreferredSize().width() +
704 // Returned height doesn't matter as MenuItemView forces everything to the
705 // height of the menuitemview. Note that we have overridden the height when
706 // constructing the menu.
707 return gfx::Size(button_width
+ zoom_label_width_
+ button_width
+
708 fullscreen_width
, 0);
711 virtual void Layout() OVERRIDE
{
713 int button_width
= std::max(increment_button_
->GetPreferredSize().width(),
714 decrement_button_
->GetPreferredSize().width());
715 gfx::Rect
bounds(0, 0, button_width
, height());
717 decrement_button_
->SetBoundsRect(bounds
);
721 bounds
.set_width(zoom_label_width_
);
722 zoom_label_
->SetBoundsRect(bounds
);
726 bounds
.set_width(button_width
);
727 increment_button_
->SetBoundsRect(bounds
);
729 x
+= bounds
.width() + (menu()->use_new_menu() ? 0 : kZoomPadding
);
731 bounds
.set_width(fullscreen_button_
->GetPreferredSize().width() +
732 (menu()->use_new_menu() ? kTouchZoomPadding
: 0));
733 fullscreen_button_
->SetBoundsRect(bounds
);
736 // Overridden from ButtonListener.
737 virtual void ButtonPressed(views::Button
* sender
,
738 const ui::Event
& event
) OVERRIDE
{
739 if (sender
->tag() == fullscreen_index_
) {
740 menu()->CancelAndEvaluate(menu_model(), sender
->tag());
742 // Zoom buttons don't close the menu.
743 menu_model()->ActivatedAt(sender
->tag());
747 // Overridden from WrenchMenuObserver.
748 virtual void WrenchMenuDestroyed() OVERRIDE
{
749 WrenchMenuView::WrenchMenuDestroyed();
753 void OnZoomLevelChanged(const HostZoomMap::ZoomLevelChange
& change
) {
754 UpdateZoomControls();
757 void UpdateZoomControls() {
758 bool enable_increment
= false;
759 bool enable_decrement
= false;
760 WebContents
* selected_tab
=
761 menu()->browser_
->tab_strip_model()->GetActiveWebContents();
764 zoom
= selected_tab
->GetZoomPercent(&enable_increment
, &enable_decrement
);
765 increment_button_
->SetEnabled(enable_increment
);
766 decrement_button_
->SetEnabled(enable_decrement
);
767 zoom_label_
->SetText(
768 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, zoom
));
770 zoom_label_width_
= MaxWidthForZoomLabel();
773 // Calculates the max width the zoom string can be.
774 int MaxWidthForZoomLabel() {
775 const gfx::FontList
& font_list
= zoom_label_
->font_list();
777 zoom_label_
->border() ? zoom_label_
->border()->GetInsets().width() : 0;
781 WebContents
* selected_tab
=
782 menu()->browser_
->tab_strip_model()->GetActiveWebContents();
784 int min_percent
= selected_tab
->GetMinimumZoomPercent();
785 int max_percent
= selected_tab
->GetMaximumZoomPercent();
787 int step
= (max_percent
- min_percent
) / 10;
788 for (int i
= min_percent
; i
<= max_percent
; i
+= step
) {
789 int w
= gfx::GetStringWidth(
790 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, i
), font_list
);
791 max_w
= std::max(w
, max_w
);
794 max_w
= gfx::GetStringWidth(
795 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT
, 100), font_list
);
798 return max_w
+ border_width
;
801 // Index of the fullscreen menu item in the model.
802 const int fullscreen_index_
;
804 scoped_ptr
<content::HostZoomMap::Subscription
> zoom_subscription_
;
805 content::NotificationRegistrar registrar_
;
807 // Button for incrementing the zoom.
808 LabelButton
* increment_button_
;
810 // Label showing zoom as a percent.
813 // Button for decrementing the zoom.
814 LabelButton
* decrement_button_
;
816 ImageButton
* fullscreen_button_
;
818 // Width given to |zoom_label_|. This is the width at 100%.
819 int zoom_label_width_
;
821 DISALLOW_COPY_AND_ASSIGN(ZoomView
);
824 // RecentTabsMenuModelDelegate ------------------------------------------------
826 // Provides the ui::MenuModelDelegate implementation for RecentTabsSubMenuModel
828 class WrenchMenu::RecentTabsMenuModelDelegate
: public ui::MenuModelDelegate
{
830 RecentTabsMenuModelDelegate(WrenchMenu
* wrench_menu
,
831 ui::MenuModel
* model
,
832 views::MenuItemView
* menu_item
)
833 : wrench_menu_(wrench_menu
),
835 menu_item_(menu_item
) {
836 model_
->SetMenuModelDelegate(this);
839 virtual ~RecentTabsMenuModelDelegate() {
840 model_
->SetMenuModelDelegate(NULL
);
843 // Return the specific menu width of recent tabs submenu if |menu| is the
844 // recent tabs submenu, else return -1.
845 int GetMaxWidthForMenu(views::MenuItemView
* menu
) {
846 if (!menu_item_
->HasSubmenu())
848 const int kMaxMenuItemWidth
= 320;
849 return menu
->GetCommand() == menu_item_
->GetCommand() ?
850 kMaxMenuItemWidth
: -1;
853 const gfx::FontList
* GetLabelFontListAt(int index
) const {
854 return model_
->GetLabelFontListAt(index
);
857 bool GetForegroundColorAt(int index
,
859 SkColor
* override_color
) const {
860 // The items for which we get a font list, should be shown in black.
861 if (GetLabelFontListAt(index
)) {
862 *override_color
= SK_ColorBLACK
;
868 // ui::MenuModelDelegate implementation:
870 virtual void OnIconChanged(int index
) OVERRIDE
{
871 int command_id
= model_
->GetCommandIdAt(index
);
872 views::MenuItemView
* item
= menu_item_
->GetMenuItemByID(command_id
);
875 model_
->GetIconAt(index
, &icon
);
876 item
->SetIcon(*icon
.ToImageSkia());
879 virtual void OnMenuStructureChanged() OVERRIDE
{
880 if (menu_item_
->HasSubmenu()) {
881 // Remove all menu items from submenu.
882 views::SubmenuView
* submenu
= menu_item_
->GetSubmenu();
883 while (submenu
->child_count() > 0)
884 menu_item_
->RemoveMenuItemAt(submenu
->child_count() - 1);
886 // Remove all elements in |WrenchMenu::command_id_to_entry_| that map to
888 WrenchMenu::CommandIDToEntry::iterator iter
=
889 wrench_menu_
->command_id_to_entry_
.begin();
890 while (iter
!= wrench_menu_
->command_id_to_entry_
.end()) {
891 if (iter
->second
.first
== model_
)
892 wrench_menu_
->command_id_to_entry_
.erase(iter
++);
898 // Add all menu items from |model| to submenu.
899 for (int i
= 0; i
< model_
->GetItemCount(); ++i
) {
900 wrench_menu_
->AddMenuItem(menu_item_
, i
, model_
, i
, model_
->GetTypeAt(i
),
904 // In case recent tabs submenu was open when items were changing, force a
905 // ChildrenChanged().
906 menu_item_
->ChildrenChanged();
910 WrenchMenu
* wrench_menu_
;
911 ui::MenuModel
* model_
;
912 views::MenuItemView
* menu_item_
;
914 DISALLOW_COPY_AND_ASSIGN(RecentTabsMenuModelDelegate
);
917 // WrenchMenu ------------------------------------------------------------------
919 WrenchMenu::WrenchMenu(Browser
* browser
,
921 bool supports_new_separators
)
924 selected_menu_model_(NULL
),
926 bookmark_menu_(NULL
),
927 feedback_menu_item_(NULL
),
928 use_new_menu_(use_new_menu
),
929 supports_new_separators_(supports_new_separators
) {
930 registrar_
.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
,
931 content::Source
<Profile
>(browser_
->profile()));
934 WrenchMenu::~WrenchMenu() {
935 if (bookmark_menu_delegate_
.get()) {
936 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(
937 browser_
->profile());
939 model
->RemoveObserver(this);
941 FOR_EACH_OBSERVER(WrenchMenuObserver
, observer_list_
, WrenchMenuDestroyed());
944 void WrenchMenu::Init(ui::MenuModel
* model
) {
946 root_
= new MenuItemView(this);
947 root_
->set_has_icons(true); // We have checks, radios and icons, set this
948 // so we get the taller menu style.
949 PopulateMenu(root_
, model
);
952 // Verify that the reserved command ID's for bookmarks menu are not used.
953 for (int i
= WrenchMenuModel
:kMinBookmarkCommandId
;
954 i
<= WrenchMenuModel::kMaxBookmarkCommandId
; ++i
)
955 DCHECK(command_id_to_entry_
.find(i
) == command_id_to_entry_
.end());
956 #endif // defined(DEBUG)
958 menu_runner_
.reset(new views::MenuRunner(root_
));
961 void WrenchMenu::RunMenu(views::MenuButton
* host
) {
962 gfx::Point screen_loc
;
963 views::View::ConvertPointToScreen(host
, &screen_loc
);
964 gfx::Rect
bounds(screen_loc
, host
->size());
965 content::RecordAction(UserMetricsAction("ShowAppMenu"));
966 if (menu_runner_
->RunMenuAt(host
->GetWidget(), host
, bounds
,
967 MenuItemView::TOPRIGHT
, ui::MENU_SOURCE_NONE
,
968 views::MenuRunner::HAS_MNEMONICS
) ==
969 views::MenuRunner::MENU_DELETED
)
971 if (bookmark_menu_delegate_
.get()) {
972 BookmarkModel
* model
= BookmarkModelFactory::GetForProfile(
973 browser_
->profile());
975 model
->RemoveObserver(this);
977 if (selected_menu_model_
)
978 selected_menu_model_
->ActivatedAt(selected_index_
);
981 bool WrenchMenu::IsShowing() {
982 return menu_runner_
.get() && menu_runner_
->IsRunning();
985 const ui::NativeTheme
* WrenchMenu::GetNativeTheme() const {
986 views::Widget
* browser_widget
= views::Widget::GetWidgetForNativeView(
987 browser_
->window()->GetNativeWindow());
988 DCHECK(browser_widget
);
989 return browser_widget
->GetNativeTheme();
992 const views::MenuConfig
& WrenchMenu::GetMenuConfig() const {
993 return MenuConfig::instance(GetNativeTheme());
996 void WrenchMenu::AddObserver(WrenchMenuObserver
* observer
) {
997 observer_list_
.AddObserver(observer
);
1000 void WrenchMenu::RemoveObserver(WrenchMenuObserver
* observer
) {
1001 observer_list_
.RemoveObserver(observer
);
1004 const gfx::FontList
* WrenchMenu::GetLabelFontList(int command_id
) const {
1005 if (IsRecentTabsCommand(command_id
)) {
1006 return recent_tabs_menu_model_delegate_
->GetLabelFontListAt(
1007 ModelIndexFromCommandId(command_id
));
1012 bool WrenchMenu::GetForegroundColor(int command_id
,
1014 SkColor
* override_color
) const {
1015 if (IsRecentTabsCommand(command_id
)) {
1016 return recent_tabs_menu_model_delegate_
->GetForegroundColorAt(
1017 ModelIndexFromCommandId(command_id
), is_hovered
, override_color
);
1022 base::string16
WrenchMenu::GetTooltipText(int command_id
,
1023 const gfx::Point
& p
) const {
1024 return IsBookmarkCommand(command_id
) ?
1025 bookmark_menu_delegate_
->GetTooltipText(command_id
, p
) : base::string16();
1028 bool WrenchMenu::IsTriggerableEvent(views::MenuItemView
* menu
,
1029 const ui::Event
& e
) {
1030 return IsBookmarkCommand(menu
->GetCommand()) ?
1031 bookmark_menu_delegate_
->IsTriggerableEvent(menu
, e
) :
1032 MenuDelegate::IsTriggerableEvent(menu
, e
);
1035 bool WrenchMenu::GetDropFormats(
1038 std::set
<ui::OSExchangeData::CustomFormat
>* custom_formats
) {
1039 CreateBookmarkMenu();
1040 return bookmark_menu_delegate_
.get() &&
1041 bookmark_menu_delegate_
->GetDropFormats(menu
, formats
, custom_formats
);
1044 bool WrenchMenu::AreDropTypesRequired(MenuItemView
* menu
) {
1045 CreateBookmarkMenu();
1046 return bookmark_menu_delegate_
.get() &&
1047 bookmark_menu_delegate_
->AreDropTypesRequired(menu
);
1050 bool WrenchMenu::CanDrop(MenuItemView
* menu
,
1051 const ui::OSExchangeData
& data
) {
1052 CreateBookmarkMenu();
1053 return bookmark_menu_delegate_
.get() &&
1054 bookmark_menu_delegate_
->CanDrop(menu
, data
);
1057 int WrenchMenu::GetDropOperation(
1059 const ui::DropTargetEvent
& event
,
1060 DropPosition
* position
) {
1061 return IsBookmarkCommand(item
->GetCommand()) ?
1062 bookmark_menu_delegate_
->GetDropOperation(item
, event
, position
) :
1063 ui::DragDropTypes::DRAG_NONE
;
1066 int WrenchMenu::OnPerformDrop(MenuItemView
* menu
,
1067 DropPosition position
,
1068 const ui::DropTargetEvent
& event
) {
1069 if (!IsBookmarkCommand(menu
->GetCommand()))
1070 return ui::DragDropTypes::DRAG_NONE
;
1072 int result
= bookmark_menu_delegate_
->OnPerformDrop(menu
, position
, event
);
1076 bool WrenchMenu::ShowContextMenu(MenuItemView
* source
,
1078 const gfx::Point
& p
,
1079 ui::MenuSourceType source_type
) {
1080 return IsBookmarkCommand(command_id
) ?
1081 bookmark_menu_delegate_
->ShowContextMenu(source
, command_id
, p
,
1086 bool WrenchMenu::CanDrag(MenuItemView
* menu
) {
1087 return IsBookmarkCommand(menu
->GetCommand()) ?
1088 bookmark_menu_delegate_
->CanDrag(menu
) : false;
1091 void WrenchMenu::WriteDragData(MenuItemView
* sender
,
1092 ui::OSExchangeData
* data
) {
1093 DCHECK(IsBookmarkCommand(sender
->GetCommand()));
1094 return bookmark_menu_delegate_
->WriteDragData(sender
, data
);
1097 int WrenchMenu::GetDragOperations(MenuItemView
* sender
) {
1098 return IsBookmarkCommand(sender
->GetCommand()) ?
1099 bookmark_menu_delegate_
->GetDragOperations(sender
) :
1100 MenuDelegate::GetDragOperations(sender
);
1103 int WrenchMenu::GetMaxWidthForMenu(MenuItemView
* menu
) {
1104 if (IsBookmarkCommand(menu
->GetCommand()))
1105 return bookmark_menu_delegate_
->GetMaxWidthForMenu(menu
);
1107 // If recent tabs menu is available, it will decide if |menu| is one of recent
1108 // tabs; if yes, it would return the menu width for recent tabs.
1109 // otherwise, it would return -1.
1110 if (recent_tabs_menu_model_delegate_
.get())
1111 max_width
= recent_tabs_menu_model_delegate_
->GetMaxWidthForMenu(menu
);
1112 if (max_width
== -1)
1113 max_width
= MenuDelegate::GetMaxWidthForMenu(menu
);
1117 bool WrenchMenu::IsItemChecked(int command_id
) const {
1118 if (IsBookmarkCommand(command_id
))
1121 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1122 return entry
.first
->IsItemCheckedAt(entry
.second
);
1125 bool WrenchMenu::IsCommandEnabled(int command_id
) const {
1126 if (IsBookmarkCommand(command_id
))
1129 if (command_id
== 0)
1130 return false; // The root item.
1132 // The items representing the cut menu (cut/copy/paste) and zoom menu
1133 // (increment/decrement/reset) are always enabled. The child views of these
1134 // items enabled state updates appropriately.
1135 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
)
1138 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1139 return entry
.first
->IsEnabledAt(entry
.second
);
1142 void WrenchMenu::ExecuteCommand(int command_id
, int mouse_event_flags
) {
1143 if (IsBookmarkCommand(command_id
)) {
1144 bookmark_menu_delegate_
->ExecuteCommand(command_id
, mouse_event_flags
);
1148 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
) {
1149 // These items are represented by child views. If ExecuteCommand is invoked
1150 // it means the user clicked on the area around the buttons and we should
1155 const Entry
& entry
= command_id_to_entry_
.find(command_id
)->second
;
1156 return entry
.first
->ActivatedAt(entry
.second
, mouse_event_flags
);
1159 bool WrenchMenu::GetAccelerator(int command_id
, ui::Accelerator
* accelerator
) {
1160 if (IsBookmarkCommand(command_id
))
1163 if (command_id
== IDC_CUT
|| command_id
== IDC_ZOOM_MINUS
) {
1164 // These have special child views; don't show the accelerator for them.
1168 CommandIDToEntry::iterator ix
= command_id_to_entry_
.find(command_id
);
1169 const Entry
& entry
= ix
->second
;
1170 ui::Accelerator menu_accelerator
;
1171 if (!entry
.first
->GetAcceleratorAt(entry
.second
, &menu_accelerator
))
1174 *accelerator
= ui::Accelerator(menu_accelerator
.key_code(),
1175 menu_accelerator
.modifiers());
1179 void WrenchMenu::WillShowMenu(MenuItemView
* menu
) {
1180 if (menu
== bookmark_menu_
)
1181 CreateBookmarkMenu();
1184 void WrenchMenu::WillHideMenu(MenuItemView
* menu
) {
1185 // Turns off the fade out animation of the wrench menus if
1186 // |feedback_menu_item_| is selected. This excludes the wrench menu itself
1187 // from the snapshot in the feedback UI.
1188 if (menu
->HasSubmenu() && feedback_menu_item_
&&
1189 feedback_menu_item_
->IsSelected()) {
1190 // It's okay to just turn off the animation and no to take care the
1191 // animation back because the menu widget will be recreated next time
1192 // it's opened. See ToolbarView::RunMenu() and Init() of this class.
1193 menu
->GetSubmenu()->GetWidget()->
1194 SetVisibilityChangedAnimationsEnabled(false);
1198 void WrenchMenu::BookmarkModelChanged() {
1199 DCHECK(bookmark_menu_delegate_
.get());
1200 if (!bookmark_menu_delegate_
->is_mutating_model())
1204 void WrenchMenu::Observe(int type
,
1205 const content::NotificationSource
& source
,
1206 const content::NotificationDetails
& details
) {
1208 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
:
1209 // A change in the global errors list can add or remove items from the
1210 // menu. Close the menu to avoid have a stale menu on-screen.
1218 void WrenchMenu::PopulateMenu(MenuItemView
* parent
,
1220 for (int i
= 0, max
= model
->GetItemCount(); i
< max
; ++i
) {
1221 // The button container menu items have a special height which we have to
1222 // use instead of the normal height.
1224 if (use_new_menu_
&&
1225 (model
->GetCommandIdAt(i
) == IDC_CUT
||
1226 model
->GetCommandIdAt(i
) == IDC_ZOOM_MINUS
))
1227 height
= kMenuItemContainingButtonsHeight
;
1229 // Add the menu item at the end.
1230 int menu_index
= parent
->HasSubmenu() ?
1231 parent
->GetSubmenu()->child_count() : 0;
1232 MenuItemView
* item
= AddMenuItem(
1233 parent
, menu_index
, model
, i
, model
->GetTypeAt(i
), height
);
1235 if (model
->GetTypeAt(i
) == MenuModel::TYPE_SUBMENU
)
1236 PopulateMenu(item
, model
->GetSubmenuModelAt(i
));
1238 const ui::NativeTheme
* native_theme
= GetNativeTheme();
1240 switch (model
->GetCommandIdAt(i
)) {
1242 DCHECK_EQ(MenuModel::TYPE_COMMAND
, model
->GetTypeAt(i
));
1243 DCHECK_LT(i
+ 2, max
);
1244 DCHECK_EQ(IDC_COPY
, model
->GetCommandIdAt(i
+ 1));
1245 DCHECK_EQ(IDC_PASTE
, model
->GetCommandIdAt(i
+ 2));
1246 item
->SetTitle(l10n_util::GetStringUTF16(IDS_EDIT2
));
1247 item
->AddChildView(new CutCopyPasteView(this, model
, native_theme
,
1252 case IDC_ZOOM_MINUS
:
1253 DCHECK_EQ(MenuModel::TYPE_COMMAND
, model
->GetTypeAt(i
));
1254 DCHECK_EQ(IDC_ZOOM_PLUS
, model
->GetCommandIdAt(i
+ 1));
1255 DCHECK_EQ(IDC_FULLSCREEN
, model
->GetCommandIdAt(i
+ 2));
1256 item
->SetTitle(l10n_util::GetStringUTF16(IDS_ZOOM_MENU2
));
1257 item
->AddChildView(new ZoomView(this, model
, native_theme
,
1262 case IDC_BOOKMARKS_MENU
:
1263 DCHECK(!bookmark_menu_
);
1264 bookmark_menu_
= item
;
1267 #if defined(GOOGLE_CHROME_BUILD)
1269 DCHECK(!feedback_menu_item_
);
1270 feedback_menu_item_
= item
;
1274 case IDC_RECENT_TABS_MENU
:
1275 DCHECK(!recent_tabs_menu_model_delegate_
.get());
1276 recent_tabs_menu_model_delegate_
.reset(
1277 new RecentTabsMenuModelDelegate(this, model
->GetSubmenuModelAt(i
),
1287 MenuItemView
* WrenchMenu::AddMenuItem(MenuItemView
* parent
,
1291 MenuModel::ItemType menu_type
,
1293 int command_id
= model
->GetCommandIdAt(model_index
);
1294 DCHECK(command_id
> -1 ||
1295 (command_id
== -1 &&
1296 model
->GetTypeAt(model_index
) == MenuModel::TYPE_SEPARATOR
));
1298 if (command_id
> -1) { // Don't add separators to |command_id_to_entry_|.
1299 // All command ID's should be unique except for IDC_SHOW_HISTORY which is
1300 // in both wrench menu and RecentTabs submenu,
1301 if (command_id
!= IDC_SHOW_HISTORY
) {
1302 DCHECK(command_id_to_entry_
.find(command_id
) ==
1303 command_id_to_entry_
.end())
1304 << "command ID " << command_id
<< " already exists!";
1306 command_id_to_entry_
[command_id
].first
= model
;
1307 command_id_to_entry_
[command_id
].second
= model_index
;
1310 MenuItemView
* menu_item
= NULL
;
1312 // For menu items with a special menu height we use our special class to be
1313 // able to modify the item height.
1314 menu_item
= new ButtonContainerMenuItemView(parent
, command_id
, height
);
1315 parent
->GetSubmenu()->AddChildViewAt(menu_item
, menu_index
);
1317 // For all other cases we use the more generic way to add menu items.
1318 menu_item
= views::MenuModelAdapter::AddMenuItemFromModelAt(
1319 model
, model_index
, parent
, menu_index
, command_id
);
1323 // Flush all buttons to the right side of the menu for the new menu type.
1324 menu_item
->set_use_right_margin(!use_new_menu_
);
1325 menu_item
->SetVisible(model
->IsVisibleAt(model_index
));
1327 if (menu_type
== MenuModel::TYPE_COMMAND
&& model
->HasIcons()) {
1329 if (model
->GetIconAt(model_index
, &icon
))
1330 menu_item
->SetIcon(*icon
.ToImageSkia());
1337 void WrenchMenu::CancelAndEvaluate(MenuModel
* model
, int index
) {
1338 selected_menu_model_
= model
;
1339 selected_index_
= index
;
1343 void WrenchMenu::CreateBookmarkMenu() {
1344 if (bookmark_menu_delegate_
.get())
1345 return; // Already created the menu.
1347 BookmarkModel
* model
=
1348 BookmarkModelFactory::GetForProfile(browser_
->profile());
1349 if (!model
->loaded())
1352 model
->AddObserver(this);
1354 // TODO(oshima): Replace with views only API.
1355 views::Widget
* parent
= views::Widget::GetWidgetForNativeWindow(
1356 browser_
->window()->GetNativeWindow());
1357 bookmark_menu_delegate_
.reset(
1358 new BookmarkMenuDelegate(browser_
,
1361 WrenchMenuModel::kMinBookmarkCommandId
,
1362 WrenchMenuModel::kMaxBookmarkCommandId
));
1363 bookmark_menu_delegate_
->Init(this,
1365 model
->bookmark_bar_node(),
1367 BookmarkMenuDelegate::SHOW_PERMANENT_FOLDERS
,
1368 BOOKMARK_LAUNCH_LOCATION_WRENCH_MENU
);
1371 int WrenchMenu::ModelIndexFromCommandId(int command_id
) const {
1372 CommandIDToEntry::const_iterator ix
= command_id_to_entry_
.find(command_id
);
1373 DCHECK(ix
!= command_id_to_entry_
.end());
1374 return ix
->second
.second
;