blob: 6d0b8cf8cf30539cd7ab1dbde9cb202ebb2e7c7c [file] [log] [blame]
// Copyright (c) 2009 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 <string>
#include "app/drag_drop_types.h"
#include "app/gfx/canvas.h"
#include "app/l10n_util.h"
#include "app/os_exchange_data.h"
#include "app/resource_bundle.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/back_forward_menu_model_views.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/character_encoding.h"
#include "chrome/browser/encoding_menu_controller.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/browser_theme_provider.h"
#include "chrome/browser/user_data_manager.h"
#include "chrome/browser/views/bookmark_menu_button.h"
#include "chrome/browser/views/event_utils.h"
#include "chrome/browser/views/go_button.h"
#include "chrome/browser/views/location_bar_view.h"
#include "chrome/browser/views/toolbar_star_toggle.h"
#include "chrome/browser/view_ids.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#ifdef CHROME_PERSONALIZATION
#include "chrome/browser/sync/personalization.h"
#endif
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/net_util.h"
#include "views/background.h"
#include "views/controls/button/button_dropdown.h"
#include "views/controls/label.h"
#if defined(OS_WIN)
#include "views/drag_utils.h"
#include "views/widget/tooltip_manager.h"
#endif
#include "views/window/non_client_view.h"
#include "views/window/window.h"
static const int kControlHorizOffset = 4;
static const int kControlVertOffset = 6;
static const int kControlIndent = 3;
static const int kStatusBubbleWidth = 480;
// Separation between the location bar and the menus.
static const int kMenuButtonOffset = 3;
// Padding to the right of the location bar
static const int kPaddingRight = 2;
static const int kPopupTopSpacingNonGlass = 3;
static const int kPopupBottomSpacingNonGlass = 2;
static const int kPopupBottomSpacingGlass = 1;
// The vertical distance between the bottom of the omnibox and the top of the
// popup.
static const int kOmniboxPopupVerticalSpacing = 2;
// The number of pixels of margin on the buttons on either side of the omnibox.
// We use this value to inset the bounds returned for the omnibox popup, since
// we want the popup to be only as wide as the visible frame of the omnibox.
static const int kOmniboxButtonsHorizontalMargin = 2;
static SkBitmap* kPopupBackgroundEdge = NULL;
////////////////////////////////////////////////////////////////////////////////
// EncodingMenuModel
EncodingMenuModel::EncodingMenuModel(Browser* browser)
: SimpleMenuModel(this),
browser_(browser) {
Build();
}
void EncodingMenuModel::Build() {
EncodingMenuController::EncodingMenuItemList encoding_menu_items;
EncodingMenuController encoding_menu_controller;
encoding_menu_controller.GetEncodingMenuItems(browser_->profile(),
&encoding_menu_items);
int group_id = 0;
EncodingMenuController::EncodingMenuItemList::iterator it =
encoding_menu_items.begin();
for (; it != encoding_menu_items.end(); ++it) {
int id = it->first;
std::wstring& label = it->second;
if (id == 0) {
AddSeparator();
} else {
if (id == IDC_ENCODING_AUTO_DETECT) {
AddCheckItem(id, WideToUTF16Hack(label));
} else {
// Use the id of the first radio command as the id of the group.
if (group_id <= 0)
group_id = id;
AddRadioItem(id, WideToUTF16Hack(label), group_id);
}
}
}
}
bool EncodingMenuModel::IsCommandIdChecked(int command_id) const {
TabContents* current_tab = browser_->GetSelectedTabContents();
EncodingMenuController controller;
return controller.IsItemChecked(browser_->profile(),
current_tab->encoding(), command_id);
}
bool EncodingMenuModel::IsCommandIdEnabled(int command_id) const {
return browser_->command_updater()->IsCommandEnabled(command_id);
}
bool EncodingMenuModel::GetAcceleratorForCommandId(
int command_id,
views::Accelerator* accelerator) {
return false;
}
void EncodingMenuModel::ExecuteCommand(int command_id) {
browser_->ExecuteCommand(command_id);
}
////////////////////////////////////////////////////////////////////////////////
// EncodingMenuModel
ZoomMenuModel::ZoomMenuModel(views::SimpleMenuModel::Delegate* delegate)
: SimpleMenuModel(delegate) {
Build();
}
void ZoomMenuModel::Build() {
AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS);
AddItemWithStringId(IDC_ZOOM_NORMAL, IDS_ZOOM_NORMAL);
AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, public:
ToolbarView::ToolbarView(Browser* browser)
: model_(browser->toolbar_model()),
acc_focused_view_(NULL),
back_(NULL),
forward_(NULL),
reload_(NULL),
home_(NULL),
star_(NULL),
location_bar_(NULL),
go_(NULL),
page_menu_(NULL),
app_menu_(NULL),
bookmark_menu_(NULL),
profile_(NULL),
browser_(browser),
profiles_menu_contents_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(
profiles_helper_(new GetProfilesHelper(this))) {
browser_->command_updater()->AddCommandObserver(IDC_BACK, this);
browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this);
browser_->command_updater()->AddCommandObserver(IDC_RELOAD, this);
browser_->command_updater()->AddCommandObserver(IDC_HOME, this);
browser_->command_updater()->AddCommandObserver(IDC_STAR, this);
if (browser->type() == Browser::TYPE_NORMAL)
display_mode_ = DISPLAYMODE_NORMAL;
else
display_mode_ = DISPLAYMODE_LOCATION;
if (!kPopupBackgroundEdge) {
kPopupBackgroundEdge = ResourceBundle::GetSharedInstance().GetBitmapNamed(
IDR_LOCATIONBG_POPUPMODE_EDGE);
}
}
ToolbarView::~ToolbarView() {
profiles_helper_->OnDelegateDeleted();
}
void ToolbarView::Init(Profile* profile) {
back_menu_model_.reset(new BackForwardMenuModelViews(
browser_, BackForwardMenuModel::BACKWARD_MENU, GetWidget()));
forward_menu_model_.reset(new BackForwardMenuModelViews(
browser_, BackForwardMenuModel::FORWARD_MENU, GetWidget()));
// Create all the individual Views in the Toolbar.
CreateLeftSideControls();
CreateCenterStack(profile);
CreateRightSideControls(profile);
show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this);
SetProfile(profile);
}
void ToolbarView::SetProfile(Profile* profile) {
if (profile == profile_)
return;
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);
}
int ToolbarView::GetNextAccessibleViewIndex(int view_index, bool nav_left) {
int modifier = 1;
if (nav_left)
modifier = -1;
int current_view_index = view_index + modifier;
while ((current_view_index >= 0) &&
(current_view_index < GetChildViewCount())) {
// Skip the location bar, as it has its own keyboard navigation. Also skip
// any views that cannot be interacted with.
if (current_view_index == GetChildIndex(location_bar_) ||
!GetChildViewAt(current_view_index)->IsEnabled() ||
!GetChildViewAt(current_view_index)->IsVisible()) {
current_view_index += modifier;
continue;
}
// Update view_index with the available button index found.
view_index = current_view_index;
break;
}
// Returns the next available button index, or if no button is available in
// the specified direction, remains where it was.
return view_index;
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, Menu::BaseControllerDelegate overrides:
bool ToolbarView::GetAcceleratorInfo(int id, views::Accelerator* accel) {
return GetWidget()->GetAccelerator(id, accel);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::MenuDelegate implementation:
void ToolbarView::RunMenu(views::View* source, const gfx::Point& pt,
gfx::NativeView parent) {
switch (source->GetID()) {
case VIEW_ID_PAGE_MENU:
RunPageMenu(pt, parent);
break;
case VIEW_ID_APP_MENU:
RunAppMenu(pt, parent);
break;
default:
NOTREACHED() << "Invalid source menu.";
}
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, GetProfilesHelper::Delegate implementation:
void ToolbarView::OnGetProfilesDone(
const std::vector<std::wstring>& profiles) {
// Nothing to do if the menu has gone away.
if (!profiles_menu_contents_.get())
return;
// Store the latest list of profiles in the browser.
browser_->set_user_data_dir_profiles(profiles);
// Add direct sub menu items for profiles.
std::vector<std::wstring>::const_iterator iter = profiles.begin();
for (int i = IDC_NEW_WINDOW_PROFILE_0;
(i <= IDC_NEW_WINDOW_PROFILE_LAST) && (iter != profiles.end());
++i, ++iter)
profiles_menu_contents_->AddItem(i, WideToUTF16Hack(*iter));
// If there are more profiles then show "Other" link.
if (iter != profiles.end()) {
profiles_menu_contents_->AddSeparator();
profiles_menu_contents_->AddItemWithStringId(IDC_SELECT_PROFILE,
IDS_SELECT_PROFILE);
}
// Always show a link to select a new profile.
profiles_menu_contents_->AddSeparator();
profiles_menu_contents_->AddItemWithStringId(
IDC_NEW_PROFILE,
IDS_SELECT_PROFILE_DIALOG_NEW_PROFILE_ENTRY);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, LocationBarView::Delegate implementation:
TabContents* ToolbarView::GetTabContents() {
return browser_->GetSelectedTabContents();
}
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_RELOAD:
button = reload_;
break;
case IDC_HOME:
button = home_;
break;
case IDC_STAR:
button = star_;
break;
}
if (button)
button->SetEnabled(enabled);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::Button::ButtonListener implementation:
void ToolbarView::ButtonPressed(views::Button* sender) {
int id = sender->tag();
switch (id) {
case IDC_BACK:
case IDC_FORWARD:
case IDC_RELOAD:
// 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();
break;
}
browser_->ExecuteCommandWithDisposition(
id, event_utils::DispositionFromEventFlags(sender->mouse_event_flags()));
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, AutocompletePopupPositioner implementation:
gfx::Rect ToolbarView::GetPopupBounds() const {
gfx::Point origin;
views::View::ConvertPointToScreen(star_, &origin);
origin.set_y(origin.y() + star_->height() + kOmniboxPopupVerticalSpacing);
gfx::Rect popup_bounds(origin.x(), origin.y(),
star_->width() + location_bar_->width() + go_->width(),
0);
if (UILayoutIsRightToLeft()) {
popup_bounds.set_x(
popup_bounds.x() - location_bar_->width() - go_->width());
} else {
popup_bounds.set_x(popup_bounds.x());
}
popup_bounds.set_y(popup_bounds.y());
popup_bounds.set_width(popup_bounds.width());
// Inset the bounds a little, since the buttons on either edge of the omnibox
// have invisible padding that makes the popup appear too wide.
popup_bounds.Inset(kOmniboxButtonsHorizontalMargin, 0);
return popup_bounds;
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, NotificationObserver implementation:
void ToolbarView::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::PREF_CHANGED) {
std::wstring* pref_name = Details<std::wstring>(details).ptr();
if (*pref_name == prefs::kShowHomeButton) {
Layout();
SchedulePaint();
}
}
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::SimpleMenuModel::Delegate implementation:
bool ToolbarView::IsCommandIdChecked(int command_id) const {
if (command_id == IDC_SHOW_BOOKMARK_BAR)
return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
return false;
}
bool ToolbarView::IsCommandIdEnabled(int command_id) const {
return browser_->command_updater()->IsCommandEnabled(command_id);
}
bool ToolbarView::GetAcceleratorForCommandId(int command_id,
views::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(L'X', false, true, false);
return true;
case IDC_COPY:
*accelerator = views::Accelerator(L'C', false, true, false);
return true;
case IDC_PASTE:
*accelerator = views::Accelerator(L'V', false, true, false);
return true;
}
// Else, we retrieve the accelerator information from the frame.
return GetWidget()->GetAccelerator(command_id, accelerator);
}
void ToolbarView::ExecuteCommand(int command_id) {
browser_->ExecuteCommand(command_id);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::View overrides:
gfx::Size ToolbarView::GetPreferredSize() {
if (IsDisplayModeNormal()) {
int min_width = kControlIndent + back_->GetPreferredSize().width() +
forward_->GetPreferredSize().width() + kControlHorizOffset +
reload_->GetPreferredSize().width() + (show_home_button_.GetValue() ?
(home_->GetPreferredSize().width() + kControlHorizOffset) : 0) +
star_->GetPreferredSize().width() + go_->GetPreferredSize().width() +
kMenuButtonOffset +
(bookmark_menu_ ? bookmark_menu_->GetPreferredSize().width() : 0) +
page_menu_->GetPreferredSize().width() +
app_menu_->GetPreferredSize().width() + kPaddingRight;
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(kControlVertOffset, height());
// We assume all child elements are the same height.
int child_height =
std::min(go_->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 + kControlIndent, child_height);
else
back_->SetBounds(kControlIndent, child_y, back_width, child_height);
forward_->SetBounds(back_->x() + back_->width(), child_y,
forward_->GetPreferredSize().width(), child_height);
reload_->SetBounds(forward_->x() + forward_->width() + kControlHorizOffset,
child_y, reload_->GetPreferredSize().width(),
child_height);
if (show_home_button_.GetValue()) {
home_->SetVisible(true);
home_->SetBounds(reload_->x() + reload_->width() + kControlHorizOffset,
child_y, home_->GetPreferredSize().width(), child_height);
} else {
home_->SetVisible(false);
home_->SetBounds(reload_->x() + reload_->width(), child_y, 0, child_height);
}
star_->SetBounds(home_->x() + home_->width() + kControlHorizOffset,
child_y, star_->GetPreferredSize().width(), child_height);
int go_button_width = go_->GetPreferredSize().width();
int page_menu_width = page_menu_->GetPreferredSize().width();
int app_menu_width = app_menu_->GetPreferredSize().width();
int bookmark_menu_width = bookmark_menu_ ?
bookmark_menu_->GetPreferredSize().width() : 0;
int location_x = star_->x() + star_->width();
int available_width = width() - kPaddingRight - bookmark_menu_width -
app_menu_width - page_menu_width - kMenuButtonOffset - go_button_width -
location_x;
location_bar_->SetBounds(location_x, child_y, std::max(available_width, 0),
child_height);
go_->SetBounds(location_bar_->x() + location_bar_->width(), child_y,
go_button_width, child_height);
int next_menu_x = go_->x() + go_->width() + kMenuButtonOffset;
if (bookmark_menu_) {
bookmark_menu_->SetBounds(next_menu_x, child_y, bookmark_menu_width,
child_height);
next_menu_x += bookmark_menu_width;
}
page_menu_->SetBounds(next_menu_x, child_y, page_menu_width, child_height);
next_menu_x += page_menu_width;
app_menu_->SetBounds(next_menu_x, 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::ThemeChanged() {
LoadLeftSideControlsImages();
LoadCenterStackImages();
LoadRightSideControlsImages();
}
void ToolbarView::ShowContextMenu(int x, int y, bool is_mouse_gesture) {
if (acc_focused_view_)
acc_focused_view_->ShowContextMenu(x, y, is_mouse_gesture);
}
void ToolbarView::DidGainFocus() {
#if defined(OS_WIN)
// Check to see if MSAA focus should be restored to previously focused button,
// and if button is an enabled, visibled child of toolbar.
if (!acc_focused_view_ ||
(acc_focused_view_->GetParent()->GetID() != VIEW_ID_TOOLBAR) ||
!acc_focused_view_->IsEnabled() ||
!acc_focused_view_->IsVisible()) {
// Find first accessible child (-1 to start search at parent).
int first_acc_child = GetNextAccessibleViewIndex(-1, false);
// No buttons enabled or visible.
if (first_acc_child == -1)
return;
set_acc_focused_view(GetChildViewAt(first_acc_child));
}
// Default focus is on the toolbar.
int view_index = VIEW_ID_TOOLBAR;
// Set hot-tracking for child, and update focused_view for MSAA focus event.
if (acc_focused_view_) {
acc_focused_view_->SetHotTracked(true);
// Show the tooltip for the view that got the focus.
if (GetWidget()->GetTooltipManager())
GetWidget()->GetTooltipManager()->ShowKeyboardTooltip(acc_focused_view_);
// Update focused_view with MSAA-adjusted child id.
view_index = acc_focused_view_->GetID();
}
gfx::NativeView wnd = GetWidget()->GetNativeView();
// Notify Access Technology that there was a change in keyboard focus.
::NotifyWinEvent(EVENT_OBJECT_FOCUS, wnd, OBJID_CLIENT,
static_cast<LONG>(view_index));
#else
// TODO(port): deal with toolbar a11y focus.
NOTIMPLEMENTED();
#endif
}
void ToolbarView::WillLoseFocus() {
#if defined(OS_WIN)
if (acc_focused_view_) {
// Resetting focus state.
acc_focused_view_->SetHotTracked(false);
}
// Any tooltips that are active should be hidden when toolbar loses focus.
if (GetWidget() && GetWidget()->GetTooltipManager())
GetWidget()->GetTooltipManager()->HideKeyboardTooltip();
#else
// TODO(port): deal with toolbar a11y focus.
NOTIMPLEMENTED();
#endif
}
bool ToolbarView::OnKeyPressed(const views::KeyEvent& e) {
#if defined(OS_WIN)
// Paranoia check, button should be initialized upon toolbar gaining focus.
if (!acc_focused_view_)
return false;
int focused_view = GetChildIndex(acc_focused_view_);
int next_view = focused_view;
switch (e.GetCharacter()) {
case VK_LEFT:
next_view = GetNextAccessibleViewIndex(focused_view, true);
break;
case VK_RIGHT:
next_view = GetNextAccessibleViewIndex(focused_view, false);
break;
case VK_DOWN:
case VK_RETURN:
// VK_SPACE is already handled by the default case.
if (acc_focused_view_->GetID() == VIEW_ID_PAGE_MENU ||
acc_focused_view_->GetID() == VIEW_ID_APP_MENU) {
// If a menu button in toolbar is activated and its menu is displayed,
// then active tooltip should be hidden.
if (GetWidget()->GetTooltipManager())
GetWidget()->GetTooltipManager()->HideKeyboardTooltip();
// Safe to cast, given to above view id check.
static_cast<views::MenuButton*>(acc_focused_view_)->Activate();
if (!acc_focused_view_) {
// Activate triggered a focus change, don't try to change focus.
return true;
}
// Re-enable hot-tracking, as Activate() will disable it.
acc_focused_view_->SetHotTracked(true);
break;
}
default:
// If key is not handled explicitly, pass it on to view.
return acc_focused_view_->OnKeyPressed(e);
}
// No buttons enabled or visible.
if (next_view == -1)
return false;
// Only send an event if focus moved.
if (next_view != focused_view) {
// Remove hot-tracking from old focused button.
acc_focused_view_->SetHotTracked(false);
// All is well, update the focused child member variable.
acc_focused_view_ = GetChildViewAt(next_view);
// Hot-track new focused button.
acc_focused_view_->SetHotTracked(true);
// Retrieve information to generate an MSAA focus event.
int view_id = acc_focused_view_->GetID();
gfx::NativeView wnd = GetWidget()->GetNativeView();
// Show the tooltip for the view that got the focus.
if (GetWidget()->GetTooltipManager()) {
GetWidget()->GetTooltipManager()->
ShowKeyboardTooltip(GetChildViewAt(next_view));
}
// Notify Access Technology that there was a change in keyboard focus.
::NotifyWinEvent(EVENT_OBJECT_FOCUS, wnd, OBJID_CLIENT,
static_cast<LONG>(view_id));
return true;
}
#else
// TODO(port): deal with toolbar a11y focus.
NOTIMPLEMENTED();
#endif
return false;
}
bool ToolbarView::OnKeyReleased(const views::KeyEvent& e) {
// Paranoia check, button should be initialized upon toolbar gaining focus.
if (!acc_focused_view_)
return false;
// Have keys be handled by the views themselves.
return acc_focused_view_->OnKeyReleased(e);
}
bool ToolbarView::GetAccessibleName(std::wstring* name) {
if (!accessible_name_.empty()) {
(*name).assign(accessible_name_);
return true;
}
return false;
}
bool ToolbarView::GetAccessibleRole(AccessibilityTypes::Role* role) {
DCHECK(role);
*role = AccessibilityTypes::ROLE_TOOLBAR;
return true;
}
void ToolbarView::SetAccessibleName(const std::wstring& name) {
accessible_name_.assign(name);
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, views::DragController implementation:
void ToolbarView::WriteDragData(views::View* sender,
int press_x,
int press_y,
OSExchangeData* data) {
DCHECK(
GetDragOperations(sender, press_x, press_y) != DragDropTypes::DRAG_NONE);
UserMetrics::RecordAction(L"Toolbar_DragStar", profile_);
#if defined(OS_WIN)
// If there is a bookmark for the URL, add the bookmark drag data for it. We
// do this to ensure the bookmark is moved, rather than creating an new
// bookmark.
TabContents* tab = browser_->GetSelectedTabContents();
if (tab) {
if (profile_ && profile_->GetBookmarkModel()) {
const BookmarkNode* node = profile_->GetBookmarkModel()->
GetMostRecentlyAddedNodeForURL(tab->GetURL());
if (node) {
BookmarkDragData bookmark_data(node);
bookmark_data.Write(profile_, data);
}
}
drag_utils::SetURLAndDragImage(tab->GetURL(),
UTF16ToWideHack(tab->GetTitle()),
tab->GetFavIcon(),
data);
}
#else
// TODO(port): do bookmark item drag & drop
NOTIMPLEMENTED();
#endif
}
int ToolbarView::GetDragOperations(views::View* sender, int x, int y) {
DCHECK(sender == star_);
TabContents* tab = browser_->GetSelectedTabContents();
if (!tab || !tab->ShouldDisplayURL() || !tab->GetURL().is_valid()) {
return DragDropTypes::DRAG_NONE;
}
if (profile_ && profile_->GetBookmarkModel() &&
profile_->GetBookmarkModel()->IsBookmarked(tab->GetURL())) {
return DragDropTypes::DRAG_MOVE | DragDropTypes::DRAG_COPY |
DragDropTypes::DRAG_LINK;
}
return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK;
}
////////////////////////////////////////////////////////////////////////////////
// ToolbarView, private:
int ToolbarView::PopupTopSpacing() const {
return GetWindow()->GetNonClientView()->UseNativeFrame() ?
0 : kPopupTopSpacingNonGlass;
}
void ToolbarView::CreateLeftSideControls() {
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);
reload_ = new views::ImageButton(this);
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);
LoadLeftSideControlsImages();
AddChildView(back_);
AddChildView(forward_);
AddChildView(reload_);
AddChildView(home_);
}
void ToolbarView::CreateCenterStack(Profile *profile) {
star_ = new ToolbarStarToggle(this, this);
star_->set_tag(IDC_STAR);
star_->SetDragController(this);
star_->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_STAR));
star_->SetToggledTooltipText(l10n_util::GetString(IDS_TOOLTIP_STARRED));
star_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_STAR));
star_->SetID(VIEW_ID_STAR_BUTTON);
AddChildView(star_);
location_bar_ = new LocationBarView(profile, browser_->command_updater(),
model_, this,
display_mode_ == DISPLAYMODE_LOCATION,
this);
// The Go button.
go_ = new GoButton(location_bar_, browser_);
go_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_GO));
go_->SetID(VIEW_ID_GO_BUTTON);
LoadCenterStackImages();
AddChildView(location_bar_);
location_bar_->Init();
AddChildView(go_);
}
void ToolbarView::CreateRightSideControls(Profile* profile) {
page_menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
page_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_PAGE));
page_menu_->SetTooltipText(l10n_util::GetString(IDS_PAGEMENU_TOOLTIP));
page_menu_->SetID(VIEW_ID_PAGE_MENU);
app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
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);
LoadRightSideControlsImages();
AddChildView(page_menu_);
AddChildView(app_menu_);
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kBookmarkMenu)) {
bookmark_menu_ = new BookmarkMenuButton(browser_);
AddChildView(bookmark_menu_);
} else {
bookmark_menu_ = NULL;
}
}
void ToolbarView::LoadLeftSideControlsImages() {
ThemeProvider* tp = GetThemeProvider();
SkColor color = tp->GetColor(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND);
SkBitmap* background = tp->GetBitmapNamed(IDR_THEME_BUTTON_BACKGROUND);
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));
back_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_BACK_MASK));
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));
forward_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_FORWARD_MASK));
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_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_BUTTON_MASK));
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));
home_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_BUTTON_MASK));
}
void ToolbarView::LoadCenterStackImages() {
ThemeProvider* tp = GetThemeProvider();
SkColor color = tp->GetColor(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND);
SkBitmap* background = tp->GetBitmapNamed(IDR_THEME_BUTTON_BACKGROUND);
star_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_STAR));
star_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_STAR_H));
star_->SetImage(views::CustomButton::BS_PUSHED,
tp->GetBitmapNamed(IDR_STAR_P));
star_->SetImage(views::CustomButton::BS_DISABLED,
tp->GetBitmapNamed(IDR_STAR_D));
star_->SetToggledImage(views::CustomButton::BS_NORMAL,
tp->GetBitmapNamed(IDR_STARRED));
star_->SetToggledImage(views::CustomButton::BS_HOT,
tp->GetBitmapNamed(IDR_STARRED_H));
star_->SetToggledImage(views::CustomButton::BS_PUSHED,
tp->GetBitmapNamed(IDR_STARRED_P));
star_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_STAR_MASK));
go_->SetImage(views::CustomButton::BS_NORMAL, tp->GetBitmapNamed(IDR_GO));
go_->SetImage(views::CustomButton::BS_HOT, tp->GetBitmapNamed(IDR_GO_H));
go_->SetImage(views::CustomButton::BS_PUSHED, tp->GetBitmapNamed(IDR_GO_P));
go_->SetToggledImage(views::CustomButton::BS_NORMAL,
tp->GetBitmapNamed(IDR_STOP));
go_->SetToggledImage(views::CustomButton::BS_HOT,
tp->GetBitmapNamed(IDR_STOP_H));
go_->SetToggledImage(views::CustomButton::BS_PUSHED,
tp->GetBitmapNamed(IDR_STOP_P));
go_->SetBackground(color, background,
tp->GetBitmapNamed(IDR_GO_MASK));
}
void ToolbarView::LoadRightSideControlsImages() {
ThemeProvider* tp = GetThemeProvider();
// We use different menu button images if the locale is right-to-left.
if (UILayoutIsRightToLeft())
page_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_PAGE_RTL));
else
page_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_PAGE));
if (UILayoutIsRightToLeft())
app_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_CHROME_RTL));
else
app_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_CHROME));
}
void ToolbarView::RunPageMenu(const gfx::Point& pt, gfx::NativeView parent) {
CreatePageMenu();
page_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
}
void ToolbarView::RunAppMenu(const gfx::Point& pt, gfx::NativeView parent) {
CreateAppMenu();
app_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
}
void ToolbarView::CreatePageMenu() {
if (page_menu_contents_.get())
return;
page_menu_contents_.reset(new views::SimpleMenuModel(this));
page_menu_contents_->AddItemWithStringId(IDC_CREATE_SHORTCUTS,
IDS_CREATE_SHORTCUTS);
page_menu_contents_->AddSeparator();
page_menu_contents_->AddItemWithStringId(IDC_CUT, IDS_CUT);
page_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
page_menu_contents_->AddItemWithStringId(IDC_PASTE, IDS_PASTE);
page_menu_contents_->AddSeparator();
page_menu_contents_->AddItemWithStringId(IDC_FIND, IDS_FIND);
page_menu_contents_->AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE);
page_menu_contents_->AddItemWithStringId(IDC_PRINT, IDS_PRINT);
page_menu_contents_->AddSeparator();
zoom_menu_contents_.reset(new ZoomMenuModel(this));
page_menu_contents_->AddSubMenuWithStringId(
IDS_ZOOM_MENU, zoom_menu_contents_.get());
encoding_menu_contents_.reset(new EncodingMenuModel(browser_));
page_menu_contents_->AddSubMenuWithStringId(
IDS_ENCODING_MENU, encoding_menu_contents_.get());
#if defined(OS_WIN)
CreateDevToolsMenuContents();
page_menu_contents_->AddSeparator();
page_menu_contents_->AddSubMenuWithStringId(
IDS_DEVELOPER_MENU, devtools_menu_contents_.get());
#endif
page_menu_contents_->AddSeparator();
page_menu_contents_->AddItemWithStringId(IDC_REPORT_BUG, IDS_REPORT_BUG);
page_menu_menu_.reset(new views::Menu2(page_menu_contents_.get()));
}
#if defined(OS_WIN)
void ToolbarView::CreateDevToolsMenuContents() {
devtools_menu_contents_.reset(new views::SimpleMenuModel(this));
devtools_menu_contents_->AddItem(IDC_VIEW_SOURCE,
l10n_util::GetString(IDS_VIEW_SOURCE));
devtools_menu_contents_->AddItem(IDC_DEV_TOOLS,
l10n_util::GetString(IDS_DEV_TOOLS));
devtools_menu_contents_->AddItem(IDC_TASK_MANAGER,
l10n_util::GetString(IDS_TASK_MANAGER));
}
#endif
void ToolbarView::CreateAppMenu() {
if (app_menu_contents_.get())
return;
app_menu_contents_.reset(new views::SimpleMenuModel(this));
app_menu_contents_->AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB);
app_menu_contents_->AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW);
app_menu_contents_->AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW,
IDS_NEW_INCOGNITO_WINDOW);
// Enumerate profiles asynchronously and then create the parent menu item.
// We will create the child menu items for this once the asynchronous call is
// done. See OnGetProfilesDone().
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kEnableUserDataDirProfiles)) {
profiles_helper_->GetProfiles(NULL);
profiles_menu_contents_.reset(new views::SimpleMenuModel(this));
app_menu_contents_->AddSubMenuWithStringId(IDS_PROFILE_MENU,
profiles_menu_contents_.get());
}
app_menu_contents_->AddSeparator();
app_menu_contents_->AddCheckItemWithStringId(IDC_SHOW_BOOKMARK_BAR,
IDS_SHOW_BOOKMARK_BAR);
app_menu_contents_->AddItemWithStringId(IDC_FULLSCREEN, IDS_FULLSCREEN);
app_menu_contents_->AddSeparator();
app_menu_contents_->AddItemWithStringId(IDC_SHOW_HISTORY, IDS_SHOW_HISTORY);
app_menu_contents_->AddItemWithStringId(IDC_SHOW_BOOKMARK_MANAGER,
IDS_BOOKMARK_MANAGER);
app_menu_contents_->AddItemWithStringId(IDC_SHOW_DOWNLOADS,
IDS_SHOW_DOWNLOADS);
app_menu_contents_->AddSeparator();
#ifdef CHROME_PERSONALIZATION
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableSync)) {
app_menu_contents_->AddItem(IDC_P13N_INFO,
Personalization::GetMenuItemInfoText(browser()));
}
#endif
app_menu_contents_->AddItemWithStringId(IDC_CLEAR_BROWSING_DATA,
IDS_CLEAR_BROWSING_DATA);
app_menu_contents_->AddItemWithStringId(IDC_IMPORT_SETTINGS,
IDS_IMPORT_SETTINGS);
app_menu_contents_->AddSeparator();
app_menu_contents_->AddItem(IDC_OPTIONS,
l10n_util::GetStringFUTF16(
IDS_OPTIONS,
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
app_menu_contents_->AddItem(IDC_ABOUT,
l10n_util::GetStringFUTF16(
IDS_ABOUT,
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
app_menu_contents_->AddItemWithStringId(IDC_HELP_PAGE, IDS_HELP_PAGE);
app_menu_contents_->AddSeparator();
app_menu_contents_->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
app_menu_menu_.reset(new views::Menu2(app_menu_contents_.get()));
}