summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/views/toolbar_view.cc
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-09 07:35:32 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-09 07:35:32 +0000
commit213dac2f0bff9162502fe325b6ebb85a255efcb2 (patch)
tree3640cb1f19976e38677b8632537d2d41f8444d0f /chrome/browser/ui/views/toolbar_view.cc
parent6de53d401aa8dc6c7e0a9874c71a95ce88ade50d (diff)
downloadchromium_src-213dac2f0bff9162502fe325b6ebb85a255efcb2.zip
chromium_src-213dac2f0bff9162502fe325b6ebb85a255efcb2.tar.gz
chromium_src-213dac2f0bff9162502fe325b6ebb85a255efcb2.tar.bz2
Move browser/views to browser/ui/views
TBR=brettw BUG=none TEST=none Review URL: http://codereview.chromium.org/4694005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65508 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/views/toolbar_view.cc')
-rw-r--r--chrome/browser/ui/views/toolbar_view.cc741
1 files changed, 741 insertions, 0 deletions
diff --git a/chrome/browser/ui/views/toolbar_view.cc b/chrome/browser/ui/views/toolbar_view.cc
new file mode 100644
index 0000000..cc82cdcd
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar_view.cc
@@ -0,0 +1,741 @@
+// Copyright (c) 2010 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/views/toolbar_view.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/accessibility/browser_accessibility_state.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/themes/browser_theme_provider.h"
+#include "chrome/browser/upgrade_detector.h"
+#include "chrome/browser/view_ids.h"
+#include "chrome/browser/views/browser_actions_container.h"
+#include "chrome/browser/views/event_utils.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/browser/wrench_menu_model.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "gfx/canvas.h"
+#include "gfx/canvas_skia.h"
+#include "gfx/skbitmap_operations.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.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 "chrome/browser/chromeos/dom_ui/wrench_menu_ui.h"
+#include "views/controls/menu/menu_2.h"
+#endif
+#include "chrome/browser/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;
+
+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),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ 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_DETECTED,
+ 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_));
+#if defined(OS_CHROMEOS)
+ if (chromeos::MenuUI::IsEnabled()) {
+ wrench_menu_2_.reset(
+ chromeos::WrenchMenuUI::CreateMenu2(wrench_menu_model_.get()));
+ }
+#endif
+ back_ = new views::ButtonDropDown(this, back_menu_model_.get());
+ back_->set_triggerable_event_flags(views::Event::EF_LEFT_BUTTON_DOWN |
+ views::Event::EF_MIDDLE_BUTTON_DOWN);
+ back_->set_tag(IDC_BACK);
+ back_->SetImageAlignment(views::ImageButton::ALIGN_RIGHT,
+ views::ImageButton::ALIGN_TOP);
+ back_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_BACK));
+ back_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_BACK));
+ back_->SetID(VIEW_ID_BACK_BUTTON);
+
+ forward_ = new views::ButtonDropDown(this, forward_menu_model_.get());
+ forward_->set_triggerable_event_flags(views::Event::EF_LEFT_BUTTON_DOWN |
+ views::Event::EF_MIDDLE_BUTTON_DOWN);
+ forward_->set_tag(IDC_FORWARD);
+ forward_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_FORWARD));
+ forward_->SetAccessibleName(l10n_util::GetString(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(views::Event::EF_LEFT_BUTTON_DOWN |
+ views::Event::EF_MIDDLE_BUTTON_DOWN);
+ reload_->set_tag(IDC_RELOAD);
+ reload_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_RELOAD));
+ reload_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_RELOAD));
+ reload_->SetID(VIEW_ID_RELOAD_BUTTON);
+
+ home_ = new views::ImageButton(this);
+ home_->set_triggerable_event_flags(views::Event::EF_LEFT_BUTTON_DOWN |
+ views::Event::EF_MIDDLE_BUTTON_DOWN);
+ home_->set_tag(IDC_HOME);
+ home_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_HOME));
+ home_->SetAccessibleName(l10n_util::GetString(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::GetString(IDS_ACCNAME_APP));
+ app_menu_->SetTooltipText(l10n_util::GetStringF(IDS_APPMENU_TOOLTIP,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ app_menu_->SetID(VIEW_ID_APP_MENU);
+
+ // Catch the case where the window is created after we detect a new version.
+ if (IsUpgradeRecommended() || ShouldShowIncompatibilityWarning())
+ ShowNotificationDot();
+
+ 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 (Singleton<BrowserAccessibilityState>()->IsAccessibleBrowser()) {
+ back_->SetTooltipText(l10n_util::GetString(IDS_ACCNAME_TOOLTIP_BACK));
+ forward_->SetTooltipText(l10n_util::GetString(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) {
+#if defined(OS_CHROMEOS)
+ if (chromeos::MenuUI::IsEnabled()) {
+ DCHECK(wrench_menu_2_.get());
+ wrench_menu_2_->AddMenuListener(listener);
+ return;
+ }
+#endif
+ menu_listeners_.push_back(listener);
+}
+
+void ToolbarView::RemoveMenuListener(views::MenuListener* listener) {
+#if defined(OS_CHROMEOS)
+ if (chromeos::MenuUI::IsEnabled()) {
+ DCHECK(wrench_menu_2_.get());
+ wrench_menu_2_->RemoveMenuListener(listener);
+ return;
+ }
+#endif
+ 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;
+}
+
+AccessibilityTypes::Role ToolbarView::GetAccessibleRole() {
+ return AccessibilityTypes::ROLE_TOOLBAR;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToolbarView, Menu::BaseControllerDelegate overrides:
+
+bool ToolbarView::GetAcceleratorInfo(int id, menus::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;
+#if defined(OS_CHROMEOS)
+ if (chromeos::MenuUI::IsEnabled()) {
+ gfx::Point screen_loc;
+ views::View::ConvertPointToScreen(app_menu_, &screen_loc);
+ gfx::Rect bounds(screen_loc, app_menu_->size());
+ if (base::i18n::IsRTL())
+ bounds.set_x(bounds.x() - app_menu_->size().width());
+ wrench_menu_2_->RunMenuAt(gfx::Point(bounds.right(), bounds.bottom()),
+ views::Menu2::ALIGN_TOPRIGHT);
+ // TODO(oshima): nuke this once we made decision about go or no go
+ // for domui menu.
+ goto cleanup;
+ }
+#endif
+ 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 defined(OS_CHROMEOS)
+cleanup:
+#endif
+ if (destroyed_flag)
+ return;
+ destroyed_flag_ = NULL;
+
+ // Stop pulsating the notification dot on the app menu (if active).
+ notification_dot_pulse_timer_.Stop();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToolbarView, LocationBarView::Delegate implementation:
+
+TabContents* ToolbarView::GetTabContents() {
+ return browser_->GetSelectedTabContents();
+}
+
+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, AnimationDelegate implementation:
+
+void ToolbarView::AnimationProgressed(const Animation* animation) {
+ 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();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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) {
+ ShowNotificationDot();
+ } else if (type == NotificationType::MODULE_INCOMPATIBILITY_DETECTED) {
+ bool confirmed_bad = *Details<bool>(details).ptr();
+ if (confirmed_bad)
+ ShowNotificationDot();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ToolbarView, menus::AcceleratorProvider implementation:
+
+bool ToolbarView::GetAcceleratorForCommandId(int command_id,
+ menus::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(app::VKEY_X, false, true, false);
+ return true;
+ case IDC_COPY:
+ *accelerator = views::Accelerator(app::VKEY_C, false, true, false);
+ return true;
+ case IDC_PASTE:
+ *accelerator = views::Accelerator(app::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()->GetNonClientView()->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;
+
+ if (!IsDisplayModeNormal()) {
+ int edge_width = (browser_->window() && browser_->window()->IsMaximized()) ?
+ 0 : kPopupBackgroundEdge->width(); // See Paint().
+ 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 (browser_->window() && browser_->window()->IsMaximized())
+ 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();
+
+ app_menu_->SetBounds(browser_actions_->x() + browser_actions_width, child_y,
+ app_menu_width, child_height);
+}
+
+void ToolbarView::Paint(gfx::Canvas* canvas) {
+ View::Paint(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.
+ if (GetWindow()->GetNonClientView()->UseNativeFrame())
+ canvas->FillRectInt(SK_ColorBLACK, 0, height() - 1, width(), 1);
+}
+
+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 (Singleton<UpgradeDetector>::get()->notify_upgrade());
+#endif
+}
+
+bool ToolbarView::ShouldShowIncompatibilityWarning() {
+#if defined(OS_WIN)
+ EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetSingleton();
+ return loaded_modules->confirmed_bad_modules_detected() > 0;
+#else
+ return false;
+#endif
+}
+
+int ToolbarView::PopupTopSpacing() const {
+ return GetWindow()->GetNonClientView()->UseNativeFrame() ?
+ 0 : kPopupTopSpacingNonGlass;
+}
+
+void ToolbarView::LoadImages() {
+ 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::ShowNotificationDot() {
+ notification_dot_animation_.reset(new SlideAnimation(this));
+ notification_dot_animation_->SetSlideDuration(kPulseDuration);
+
+ // Then start the recurring timer for pulsating it.
+ notification_dot_pulse_timer_.Stop();
+ notification_dot_pulse_timer_.Start(
+ base::TimeDelta::FromMilliseconds(kPulsateEveryMs),
+ this, &ToolbarView::PulsateNotificationDot);
+}
+
+void ToolbarView::PulsateNotificationDot() {
+ // Start the pulsating animation.
+ notification_dot_animation_->Reset(0.0);
+ notification_dot_animation_->Show();
+}
+
+SkBitmap ToolbarView::GetAppMenuIcon(views::CustomButton::ButtonState state) {
+ 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);
+
+ 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;
+ static bool has_faded_in = false;
+ if (!has_faded_in) {
+ SkBitmap* dot = NULL;
+ if (ShouldShowIncompatibilityWarning()) {
+#if defined(OS_WIN)
+ dot = tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE);
+ }
+ SkBitmap transparent;
+ transparent.setConfig(dot->getConfig(), dot->width(), dot->height());
+ transparent.allocPixels();
+ transparent.eraseARGB(0, 0, 0, 0);
+ badge = SkBitmapOperations::CreateBlendedBitmap(
+ *dot, transparent,
+ 1.0 - notification_dot_animation_->GetCurrentValue());
+ if (notification_dot_animation_->GetCurrentValue() == 1.0)
+ has_faded_in = true;
+ } else {
+ // Convert animation values that start from 0.0 and incrementally go
+ // up to 1.0 into values that start in 0.0, go to 1.0 and then back
+ // to 0.0 (to create a pulsing effect).
+ double value =
+ 1.0 - abs(2.0 * notification_dot_animation_->GetCurrentValue() - 1.0);
+
+ // Add the badge to it.
+ if (ShouldShowIncompatibilityWarning()) {
+#if defined(OS_WIN)
+ badge = SkBitmapOperations::CreateBlendedBitmap(
+ *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_INACTIVE),
+ *tp->GetBitmapNamed(IDR_INCOMPATIBILITY_DOT_ACTIVE),
+ value);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ badge = SkBitmapOperations::CreateBlendedBitmap(
+ *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE),
+ *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE),
+ value);
+ }
+ }
+
+ static const int kBadgeLeftSpacing = 8;
+ static const int kBadgeTopSpacing = 18;
+ canvas->DrawBitmapInt(badge, kBadgeLeftSpacing, kBadgeTopSpacing);
+
+ return canvas->ExtractBitmap();
+}