// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/views/toolbar_view.h" #include "base/i18n/number_formatting.h" #include "base/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/accessibility/browser_accessibility_state.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/toolbar/wrench_menu_model.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/browser_actions_container.h" #include "chrome/browser/ui/views/event_utils.h" #include "chrome/browser/upgrade_detector.h" #include "chrome/common/pref_names.h" #include "content/common/notification_service.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/accessibility/accessible_view_state.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/theme_provider.h" #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia.h" #include "ui/gfx/skbitmap_operations.h" #include "views/controls/button/button_dropdown.h" #include "views/focus/view_storage.h" #include "views/widget/tooltip_manager.h" #include "views/window/non_client_view.h" #include "views/window/window.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/update_library.h" #include "views/controls/menu/menu_2.h" #endif #include "chrome/browser/ui/views/wrench_menu.h" #if defined(OS_WIN) #include "chrome/browser/enumerate_modules_model_win.h" #endif // The space between items is 4 px in general. const int ToolbarView::kStandardSpacing = 4; // The top of the toolbar has an edge we have to skip over in addition to the 4 // px of spacing. const int ToolbarView::kVertSpacing = kStandardSpacing + 1; // The edge graphics have some built-in spacing/shadowing, so we have to adjust // our spacing to make it still appear to be 4 px. static const int kEdgeSpacing = ToolbarView::kStandardSpacing - 1; // The buttons to the left of the omnibox are close together. static const int kButtonSpacing = 1; static const int kStatusBubbleWidth = 480; // The length of time to run the upgrade notification animation (the time it // takes one pulse to run its course and go back to its original brightness). static const int kPulseDuration = 2000; // How long to wait between pulsating the upgrade notifier. static const int kPulsateEveryMs = 8000; static const int kPopupTopSpacingNonGlass = 3; static const int kPopupBottomSpacingNonGlass = 2; static const int kPopupBottomSpacingGlass = 1; // Top margin for the wrench menu badges (badge is placed in the upper right // corner of the wrench menu). static const int kBadgeTopMargin = 2; static SkBitmap* kPopupBackgroundEdge = NULL; //////////////////////////////////////////////////////////////////////////////// // ToolbarView, public: ToolbarView::ToolbarView(Browser* browser) : model_(browser->toolbar_model()), back_(NULL), forward_(NULL), reload_(NULL), home_(NULL), location_bar_(NULL), browser_actions_(NULL), app_menu_(NULL), profile_(NULL), browser_(browser), profiles_menu_contents_(NULL), destroyed_flag_(NULL) { SetID(VIEW_ID_TOOLBAR); browser_->command_updater()->AddCommandObserver(IDC_BACK, this); browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this); browser_->command_updater()->AddCommandObserver(IDC_HOME, this); display_mode_ = browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ? DISPLAYMODE_NORMAL : DISPLAYMODE_LOCATION; if (!kPopupBackgroundEdge) { kPopupBackgroundEdge = ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_LOCATIONBG_POPUPMODE_EDGE); } if (!IsUpgradeRecommended()) { registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, NotificationService::AllSources()); } registrar_.Add(this, NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE, NotificationService::AllSources()); } ToolbarView::~ToolbarView() { if (destroyed_flag_) *destroyed_flag_ = true; // NOTE: Don't remove the command observers here. This object gets destroyed // after the Browser (which owns the CommandUpdater), so the CommandUpdater is // already gone. } void ToolbarView::Init(Profile* profile) { back_menu_model_.reset(new BackForwardMenuModel( browser_, BackForwardMenuModel::BACKWARD_MENU)); forward_menu_model_.reset(new BackForwardMenuModel( browser_, BackForwardMenuModel::FORWARD_MENU)); wrench_menu_model_.reset(new WrenchMenuModel(this, browser_)); back_ = new views::ButtonDropDown(this, back_menu_model_.get()); back_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | ui::EF_MIDDLE_BUTTON_DOWN); back_->set_tag(IDC_BACK); back_->SetImageAlignment(views::ImageButton::ALIGN_RIGHT, views::ImageButton::ALIGN_TOP); back_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_BACK))); back_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK)); back_->SetID(VIEW_ID_BACK_BUTTON); forward_ = new views::ButtonDropDown(this, forward_menu_model_.get()); forward_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | ui::EF_MIDDLE_BUTTON_DOWN); forward_->set_tag(IDC_FORWARD); forward_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_FORWARD))); forward_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_FORWARD)); forward_->SetID(VIEW_ID_FORWARD_BUTTON); // Have to create this before |reload_| as |reload_|'s constructor needs it. location_bar_ = new LocationBarView(profile, browser_->command_updater(), model_, this, (display_mode_ == DISPLAYMODE_LOCATION) ? LocationBarView::POPUP : LocationBarView::NORMAL); reload_ = new ReloadButton(location_bar_, browser_); reload_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | ui::EF_MIDDLE_BUTTON_DOWN); reload_->set_tag(IDC_RELOAD); reload_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_RELOAD))); reload_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_RELOAD)); reload_->SetID(VIEW_ID_RELOAD_BUTTON); home_ = new views::ImageButton(this); home_->set_triggerable_event_flags(ui::EF_LEFT_BUTTON_DOWN | ui::EF_MIDDLE_BUTTON_DOWN); home_->set_tag(IDC_HOME); home_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_TOOLTIP_HOME))); home_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_HOME)); home_->SetID(VIEW_ID_HOME_BUTTON); browser_actions_ = new BrowserActionsContainer(browser_, this); app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false); app_menu_->set_border(NULL); app_menu_->EnableCanvasFlippingForRTLUI(true); app_menu_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_APP)); app_menu_->SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16( IDS_APPMENU_TOOLTIP, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)))); app_menu_->SetID(VIEW_ID_APP_MENU); // Add any necessary badges to the menu item based on the system state. if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning()) { UpdateAppMenuBadge(); } LoadImages(); // Always add children in order from left to right, for accessibility. AddChildView(back_); AddChildView(forward_); AddChildView(reload_); AddChildView(home_); AddChildView(location_bar_); AddChildView(browser_actions_); AddChildView(app_menu_); location_bar_->Init(); show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this); browser_actions_->Init(); SetProfile(profile); // Accessibility specific tooltip text. if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) { back_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_BACK))); forward_->SetTooltipText( UTF16ToWide(l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLTIP_FORWARD))); } } void ToolbarView::SetProfile(Profile* profile) { if (profile != profile_) { profile_ = profile; location_bar_->SetProfile(profile); } } void ToolbarView::Update(TabContents* tab, bool should_restore_state) { if (location_bar_) location_bar_->Update(should_restore_state ? tab : NULL); if (browser_actions_) browser_actions_->RefreshBrowserActionViews(); } void ToolbarView::SetPaneFocusAndFocusLocationBar(int view_storage_id) { SetPaneFocus(view_storage_id, location_bar_); } void ToolbarView::SetPaneFocusAndFocusAppMenu(int view_storage_id) { SetPaneFocus(view_storage_id, app_menu_); } bool ToolbarView::IsAppMenuFocused() { return app_menu_->HasFocus(); } void ToolbarView::AddMenuListener(views::MenuListener* listener) { menu_listeners_.push_back(listener); } void ToolbarView::RemoveMenuListener(views::MenuListener* listener) { for (std::vector<views::MenuListener*>::iterator i(menu_listeners_.begin()); i != menu_listeners_.end(); ++i) { if (*i == listener) { menu_listeners_.erase(i); return; } } } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, AccessiblePaneView overrides: bool ToolbarView::SetPaneFocus( int view_storage_id, views::View* initial_focus) { if (!AccessiblePaneView::SetPaneFocus(view_storage_id, initial_focus)) return false; location_bar_->SetShowFocusRect(true); return true; } void ToolbarView::GetAccessibleState(ui::AccessibleViewState* state) { state->role = ui::AccessibilityTypes::ROLE_TOOLBAR; state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_TOOLBAR); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, Menu::Delegate overrides: bool ToolbarView::GetAcceleratorInfo(int id, ui::Accelerator* accel) { return GetWidget()->GetAccelerator(id, accel); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, views::MenuDelegate implementation: void ToolbarView::RunMenu(views::View* source, const gfx::Point& /* pt */) { DCHECK_EQ(VIEW_ID_APP_MENU, source->GetID()); bool destroyed_flag = false; destroyed_flag_ = &destroyed_flag; wrench_menu_ = new WrenchMenu(browser_); wrench_menu_->Init(wrench_menu_model_.get()); for (size_t i = 0; i < menu_listeners_.size(); ++i) menu_listeners_[i]->OnMenuOpened(); wrench_menu_->RunMenu(app_menu_); if (destroyed_flag) return; destroyed_flag_ = NULL; } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, LocationBarView::Delegate implementation: TabContentsWrapper* ToolbarView::GetTabContentsWrapper() const { return browser_->GetSelectedTabContentsWrapper(); } InstantController* ToolbarView::GetInstant() { return browser_->instant(); } void ToolbarView::OnInputInProgress(bool in_progress) { // The edit should make sure we're only notified when something changes. DCHECK(model_->input_in_progress() != in_progress); model_->set_input_in_progress(in_progress); location_bar_->Update(NULL); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, CommandUpdater::CommandObserver implementation: void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) { views::Button* button = NULL; switch (id) { case IDC_BACK: button = back_; break; case IDC_FORWARD: button = forward_; break; case IDC_HOME: button = home_; break; } if (button) button->SetEnabled(enabled); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, views::Button::ButtonListener implementation: void ToolbarView::ButtonPressed(views::Button* sender, const views::Event& event) { int command = sender->tag(); WindowOpenDisposition disposition = event_utils::DispositionFromEventFlags(sender->mouse_event_flags()); if ((disposition == CURRENT_TAB) && ((command == IDC_BACK) || (command == IDC_FORWARD))) { // Forcibly reset the location bar, since otherwise it won't discard any // ongoing user edits, since it doesn't realize this is a user-initiated // action. location_bar_->Revert(); } browser_->ExecuteCommandWithDisposition(command, disposition); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, NotificationObserver implementation: void ToolbarView::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::PREF_CHANGED) { std::string* pref_name = Details<std::string>(details).ptr(); if (*pref_name == prefs::kShowHomeButton) { Layout(); SchedulePaint(); } } else if (type == NotificationType::UPGRADE_RECOMMENDED || type == NotificationType::MODULE_INCOMPATIBILITY_BADGE_CHANGE) { UpdateAppMenuBadge(); } } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, ui::AcceleratorProvider implementation: bool ToolbarView::GetAcceleratorForCommandId(int command_id, ui::Accelerator* accelerator) { // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators // anywhere so we need to check for them explicitly here. // TODO(cpu) Bug 1109102. Query WebKit land for the actual bindings. switch (command_id) { case IDC_CUT: *accelerator = views::Accelerator(ui::VKEY_X, false, true, false); return true; case IDC_COPY: *accelerator = views::Accelerator(ui::VKEY_C, false, true, false); return true; case IDC_PASTE: *accelerator = views::Accelerator(ui::VKEY_V, false, true, false); return true; } // Else, we retrieve the accelerator information from the frame. return GetWidget()->GetAccelerator(command_id, accelerator); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, views::View overrides: gfx::Size ToolbarView::GetPreferredSize() { if (IsDisplayModeNormal()) { int min_width = kEdgeSpacing + back_->GetPreferredSize().width() + kButtonSpacing + forward_->GetPreferredSize().width() + kButtonSpacing + reload_->GetPreferredSize().width() + kStandardSpacing + (show_home_button_.GetValue() ? (home_->GetPreferredSize().width() + kButtonSpacing) : 0) + browser_actions_->GetPreferredSize().width() + app_menu_->GetPreferredSize().width() + kEdgeSpacing; static SkBitmap normal_background; if (normal_background.isNull()) { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); normal_background = *rb.GetBitmapNamed(IDR_CONTENT_TOP_CENTER); } return gfx::Size(min_width, normal_background.height()); } int vertical_spacing = PopupTopSpacing() + (GetWindow()->non_client_view()->UseNativeFrame() ? kPopupBottomSpacingGlass : kPopupBottomSpacingNonGlass); return gfx::Size(0, location_bar_->GetPreferredSize().height() + vertical_spacing); } void ToolbarView::Layout() { // If we have not been initialized yet just do nothing. if (back_ == NULL) return; bool maximized = browser_->window() && browser_->window()->IsMaximized(); if (!IsDisplayModeNormal()) { int edge_width = maximized ? 0 : kPopupBackgroundEdge->width(); // See OnPaint(). location_bar_->SetBounds(edge_width, PopupTopSpacing(), width() - (edge_width * 2), location_bar_->GetPreferredSize().height()); return; } int child_y = std::min(kVertSpacing, height()); // We assume all child elements are the same height. int child_height = std::min(back_->GetPreferredSize().height(), height() - child_y); // If the window is maximized, we extend the back button to the left so that // clicking on the left-most pixel will activate the back button. // TODO(abarth): If the window becomes maximized but is not resized, // then Layout() might not be called and the back button // will be slightly the wrong size. We should force a // Layout() in this case. // http://crbug.com/5540 int back_width = back_->GetPreferredSize().width(); if (maximized) back_->SetBounds(0, child_y, back_width + kEdgeSpacing, child_height); else back_->SetBounds(kEdgeSpacing, child_y, back_width, child_height); forward_->SetBounds(back_->x() + back_->width() + kButtonSpacing, child_y, forward_->GetPreferredSize().width(), child_height); reload_->SetBounds(forward_->x() + forward_->width() + kButtonSpacing, child_y, reload_->GetPreferredSize().width(), child_height); if (show_home_button_.GetValue()) { home_->SetVisible(true); home_->SetBounds(reload_->x() + reload_->width() + kButtonSpacing, child_y, home_->GetPreferredSize().width(), child_height); } else { home_->SetVisible(false); home_->SetBounds(reload_->x() + reload_->width(), child_y, 0, child_height); } int browser_actions_width = browser_actions_->GetPreferredSize().width(); int app_menu_width = app_menu_->GetPreferredSize().width(); int location_x = home_->x() + home_->width() + kStandardSpacing; int available_width = width() - kEdgeSpacing - app_menu_width - browser_actions_width - location_x; location_bar_->SetBounds(location_x, child_y, std::max(available_width, 0), child_height); browser_actions_->SetBounds(location_bar_->x() + location_bar_->width(), 0, browser_actions_width, height()); // The browser actions need to do a layout explicitly, because when an // extension is loaded/unloaded/changed, BrowserActionContainer removes and // re-adds everything, regardless of whether it has a page action. For a // page action, browser action bounds do not change, as a result of which // SetBounds does not do a layout at all. // TODO(sidchat): Rework the above behavior so that explicit layout is not // required. browser_actions_->Layout(); // Extend the app menu to the screen's right edge in maximized mode just like // we extend the back button to the left edge. if (maximized) app_menu_width += kEdgeSpacing; app_menu_->SetBounds(browser_actions_->x() + browser_actions_width, child_y, app_menu_width, child_height); } void ToolbarView::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); if (IsDisplayModeNormal()) return; // In maximized mode, we don't draw the endcaps on the location bar, because // when they're flush against the edge of the screen they just look glitchy. if (!browser_->window() || !browser_->window()->IsMaximized()) { int top_spacing = PopupTopSpacing(); canvas->DrawBitmapInt(*kPopupBackgroundEdge, 0, top_spacing); canvas->DrawBitmapInt(*kPopupBackgroundEdge, width() - kPopupBackgroundEdge->width(), top_spacing); } // For glass, we need to draw a black line below the location bar to separate // it from the content area. For non-glass, the NonClientView draws the // toolbar background below the location bar for us. // NOTE: Keep this in sync with BrowserView::GetInfoBarSeparatorColor()! if (GetWindow()->non_client_view()->UseNativeFrame()) canvas->FillRectInt(SK_ColorBLACK, 0, height() - 1, width(), 1); } // Note this method is ignored on Windows, but needs to be implemented for // linux, where it is called before CanDrop(). bool ToolbarView::GetDropFormats( int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) { *formats = ui::OSExchangeData::URL | ui::OSExchangeData::STRING; return true; } bool ToolbarView::CanDrop(const ui::OSExchangeData& data) { // To support loading URLs by dropping into the toolbar, we need to support // dropping URLs and/or text. return data.HasURL() || data.HasString(); } int ToolbarView::OnDragUpdated(const views::DropTargetEvent& event) { if (event.source_operations() & ui::DragDropTypes::DRAG_COPY) { return ui::DragDropTypes::DRAG_COPY; } else if (event.source_operations() & ui::DragDropTypes::DRAG_LINK) { return ui::DragDropTypes::DRAG_LINK; } return ui::DragDropTypes::DRAG_NONE; } int ToolbarView::OnPerformDrop(const views::DropTargetEvent& event) { return location_bar_->location_entry()->OnPerformDrop(event); } void ToolbarView::OnThemeChanged() { LoadImages(); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, protected: // Override this so that when the user presses F6 to rotate toolbar panes, // the location bar gets focus, not the first control in the toolbar. views::View* ToolbarView::GetDefaultFocusableChild() { return location_bar_; } void ToolbarView::RemovePaneFocus() { AccessiblePaneView::RemovePaneFocus(); location_bar_->SetShowFocusRect(false); } //////////////////////////////////////////////////////////////////////////////// // ToolbarView, private: bool ToolbarView::IsUpgradeRecommended() { #if defined(OS_CHROMEOS) return (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status == chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT); #else return (UpgradeDetector::GetInstance()->notify_upgrade()); #endif } int ToolbarView::GetUpgradeRecommendedBadge() const { #if defined(OS_CHROMEOS) return IDR_UPDATE_BADGE; #else switch (UpgradeDetector::GetInstance()->upgrade_notification_stage()) { case UpgradeDetector::UPGRADE_ANNOYANCE_SEVERE: return IDR_UPDATE_BADGE4; case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH: return IDR_UPDATE_BADGE3; case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED: return IDR_UPDATE_BADGE2; default: return IDR_UPDATE_BADGE; } #endif } bool ToolbarView::ShouldShowIncompatibilityWarning() { #if defined(OS_WIN) EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance(); return loaded_modules->ShouldShowConflictWarning(); #else return false; #endif } int ToolbarView::PopupTopSpacing() const { // TODO(beng): For some reason GetWindow() returns NULL here in some // unidentified circumstances on ChromeOS. This means GetWidget() // succeeded but we were (probably) unable to locate a WidgetGtk* // on it using NativeWidget::GetNativeWidgetForNativeView. // I am throwing in a NULL check for now to stop the hurt, but // it's possible the crash may just show up somewhere else. const views::Window* window = GetWindow(); DCHECK(window) << "If you hit this please talk to beng"; return window && window->non_client_view()->UseNativeFrame() ? 0 : kPopupTopSpacingNonGlass; } void ToolbarView::LoadImages() { ui::ThemeProvider* tp = GetThemeProvider(); back_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_BACK)); back_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_BACK_H)); back_->SetImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_BACK_P)); back_->SetImage(views::CustomButton::BS_DISABLED, tp->GetBitmapNamed(IDR_BACK_D)); forward_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_FORWARD)); forward_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_FORWARD_H)); forward_->SetImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_FORWARD_P)); forward_->SetImage(views::CustomButton::BS_DISABLED, tp->GetBitmapNamed(IDR_FORWARD_D)); reload_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_RELOAD)); reload_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_RELOAD_H)); reload_->SetImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_RELOAD_P)); reload_->SetToggledImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_STOP)); reload_->SetToggledImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_STOP_H)); reload_->SetToggledImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_STOP_P)); reload_->SetToggledImage(views::CustomButton::BS_DISABLED, tp->GetBitmapNamed(IDR_STOP_D)); home_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_HOME)); home_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_HOME_H)); home_->SetImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_HOME_P)); app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL)); app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT)); app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED)); } void ToolbarView::UpdateAppMenuBadge() { app_menu_->SetIcon(GetAppMenuIcon(views::CustomButton::BS_NORMAL)); app_menu_->SetHoverIcon(GetAppMenuIcon(views::CustomButton::BS_HOT)); app_menu_->SetPushedIcon(GetAppMenuIcon(views::CustomButton::BS_PUSHED)); SchedulePaint(); } SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) { ui::ThemeProvider* tp = GetThemeProvider(); int id = 0; switch (state) { case views::CustomButton::BS_NORMAL: id = IDR_TOOLS; break; case views::CustomButton::BS_HOT: id = IDR_TOOLS_H; break; case views::CustomButton::BS_PUSHED: id = IDR_TOOLS_P; break; default: NOTREACHED(); break; } SkBitmap icon = *tp->GetBitmapNamed(id); #if defined(OS_WIN) // Keep track of whether we were showing the badge before, so we don't send // multiple UMA events for example when multiple Chrome windows are open. static bool incompatibility_badge_showing = false; // Save the old value before resetting it. bool was_showing = incompatibility_badge_showing; incompatibility_badge_showing = false; #endif bool add_badge = IsUpgradeRecommended() || ShouldShowIncompatibilityWarning(); if (!add_badge) return icon; // Draw the chrome app menu icon onto the canvas. scoped_ptr<gfx::CanvasSkia> canvas( new gfx::CanvasSkia(icon.width(), icon.height(), false)); canvas->DrawBitmapInt(icon, 0, 0); SkBitmap badge; // Only one badge can be active at any given time. The Upgrade notification // is deemed most important, then the DLL conflict badge. if (IsUpgradeRecommended()) { badge = *tp->GetBitmapNamed(GetUpgradeRecommendedBadge()); } else if (ShouldShowIncompatibilityWarning()) { #if defined(OS_WIN) if (!was_showing) UserMetrics::RecordAction(UserMetricsAction("ConflictBadge"), profile_); badge = *tp->GetBitmapNamed(IDR_CONFLICT_BADGE); incompatibility_badge_showing = true; #else NOTREACHED(); #endif } else { NOTREACHED(); } canvas->DrawBitmapInt(badge, icon.width() - badge.width(), kBadgeTopMargin); return canvas->ExtractBitmap(); }