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