blob: da150e828d2279dc0707065af7714d22c3d750a5 [file] [log] [blame]
// Copyright 2013 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/profile_reset_bubble_view.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profile_resetter/profile_reset_global_error.h"
#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/profile_reset_bubble.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar_view.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/user_metrics.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
using views::GridLayout;
namespace {
// Fixed width of the column holding the description label of the bubble.
const int kWidthOfDescriptionText = 370;
// Margins width for the top rows to compensate for the bottom panel for which
// we don't want any margin.
const int kMarginWidth = 12;
const int kMarginHeight = kMarginWidth;
// Width of a colum in the FeedbackView.
const int kFeedbackViewColumnWidth = kWidthOfDescriptionText / 2 + kMarginWidth;
// Width of the column used to disaplay the help button.
const int kHelpButtonColumnWidth = 30;
// Width of the reporting checkbox column.
const int kReportingCheckboxColumnWidth =
kWidthOfDescriptionText + 2 * kMarginWidth;
// Full width including all columns.
const int kAllColumnsWidth =
kReportingCheckboxColumnWidth + kHelpButtonColumnWidth;
// Maximum height of the scrollable feedback view.
const int kMaxFeedbackViewHeight = 450;
// The vertical padding between two values in the feedback view.
const int kInterFeedbackValuePadding = 4;
// We subtract 2 to account for the natural button padding, and
// to bring the separation visually in line with the row separation
// height.
const int kButtonPadding = views::kRelatedButtonHSpacing - 2;
// The color of the background of the sub panel to report current settings.
const SkColor kLightGrayBackgroundColor = 0xFFF5F5F5;
// This view is used to contain the scrollable contents that are shown the user
// to expose what feedback will be sent back to Google.
class FeedbackView : public views::View {
public:
FeedbackView() {}
// Setup the layout manager of the Feedback view using the content of the
// |feedback| ListValue which contains a list of key/value pairs stored in
// DictionaryValues. The key is to be displayed right aligned on the left, and
// the value as a left aligned multiline text on the right.
void SetupLayoutManager(const ListValue& feedback) {
RemoveAllChildViews(true);
set_background(views::Background::CreateSolidBackground(
kLightGrayBackgroundColor));
GridLayout* layout = new GridLayout(this);
SetLayoutManager(layout);
// We only need a single column set for left/right text and middle margin.
views::ColumnSet* cs = layout->AddColumnSet(0);
cs->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1,
GridLayout::FIXED, kFeedbackViewColumnWidth, 0);
cs->AddPaddingColumn(0, kMarginWidth);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
GridLayout::FIXED, kFeedbackViewColumnWidth, 0);
for (size_t i = 0; i < feedback.GetSize(); ++i) {
const DictionaryValue* dictionary = NULL;
if (!feedback.GetDictionary(i, &dictionary) || !dictionary)
continue;
string16 key;
if (!dictionary->GetString("key", &key))
continue;
string16 value;
if (!dictionary->GetString("value", &value))
continue;
// The key is shown on the left, single line, right-aligned.
views::Label* left_text_label = new views::Label(key);
left_text_label->SetEnabledColor(SK_ColorGRAY);
left_text_label->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
// The value is shown on the right, multi-line, left-aligned.
views::Label* right_text_label = new views::Label(value);
right_text_label->SetMultiLine(true);
right_text_label->SetEnabledColor(SK_ColorDKGRAY);
right_text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
layout->StartRow(0, 0);
layout->AddView(left_text_label);
layout->AddView(right_text_label);
layout->AddPaddingRow(0, kInterFeedbackValuePadding);
}
// We need to set our size to our preferred size because our parent is a
// scroll view and doesn't know which size to set us to. Also since our
// parent scrolls, we are not bound to its size. So our size is based on the
// size computed by the our layout manager, which is what
// SizeToPreferredSize() does.
SizeToPreferredSize();
}
private:
DISALLOW_COPY_AND_ASSIGN(FeedbackView);
};
} // namespace
// ProfileResetBubbleView ---------------------------------------------------
// static
ProfileResetBubbleView* ProfileResetBubbleView::ShowBubble(
const base::WeakPtr<ProfileResetGlobalError>& global_error,
Browser* browser) {
views::View* anchor_view =
BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu();
ProfileResetBubbleView* reset_bubble = new ProfileResetBubbleView(
global_error, anchor_view, browser, browser->profile());
views::BubbleDelegateView::CreateBubble(reset_bubble);
reset_bubble->StartFade(true);
content::RecordAction(content::UserMetricsAction("SettingsResetBubble.Show"));
return reset_bubble;
}
ProfileResetBubbleView::~ProfileResetBubbleView() {}
views::View* ProfileResetBubbleView::GetInitiallyFocusedView() {
return controls_.reset_button;
}
void ProfileResetBubbleView::WindowClosing() {
if (global_error_)
global_error_->OnBubbleViewDidClose();
}
ProfileResetBubbleView::ProfileResetBubbleView(
const base::WeakPtr<ProfileResetGlobalError>& global_error,
views::View* anchor_view,
content::PageNavigator* navigator,
Profile* profile)
: BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
navigator_(navigator),
profile_(profile),
global_error_(global_error),
resetting_(false),
chose_to_reset_(false),
show_help_pane_(false),
weak_factory_(this) {
}
void ProfileResetBubbleView::ResetAllChildren() {
controls_.Reset();
SetLayoutManager(NULL);
RemoveAllChildViews(true);
}
void ProfileResetBubbleView::Init() {
set_margins(gfx::Insets(kMarginHeight, 0, 0, 0));
SetupLayoutManager(true);
}
void ProfileResetBubbleView::SetupLayoutManager(bool report_checked) {
ResetAllChildren();
string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
// Bubble title label.
views::Label* title_label = new views::Label(
l10n_util::GetStringFUTF16(IDS_RESET_BUBBLE_TITLE, product_name));
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label->SetFont(rb.GetFont(ui::ResourceBundle::BoldFont));
// Description text label.
views::Label* text_label = new views::Label(
l10n_util::GetStringFUTF16(IDS_RESET_BUBBLE_TEXT, product_name));
text_label->SetMultiLine(true);
text_label->SetLineHeight(20);
text_label->SetEnabledColor(SK_ColorDKGRAY);
text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
// Learn more link.
views::Link* learn_more_link = new views::Link(
l10n_util::GetStringUTF16(IDS_LEARN_MORE));
learn_more_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
learn_more_link->set_listener(this);
learn_more_link->SetUnderline(false);
// Reset button's name is based on |resetting_| state.
int reset_button_string_id = IDS_RESET_PROFILE_SETTINGS_COMMIT_BUTTON;
if (resetting_)
reset_button_string_id = IDS_RESETTING;
controls_.reset_button = new views::LabelButton(
this, l10n_util::GetStringUTF16(reset_button_string_id));
controls_.reset_button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
controls_.reset_button->SetIsDefault(true);
controls_.reset_button->SetFont(rb.GetFont(ui::ResourceBundle::BoldFont));
controls_.reset_button->SetEnabled(!resetting_);
// For the Resetting... text to fit.
gfx::Size reset_button_size = controls_.reset_button->GetPreferredSize();
reset_button_size.set_width(100);
controls_.reset_button->set_min_size(reset_button_size);
// No thanks button.
controls_.no_thanks_button = new views::LabelButton(
this, l10n_util::GetStringUTF16(IDS_NO_THANKS));
controls_.no_thanks_button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
controls_.no_thanks_button->SetEnabled(!resetting_);
// Checkbox for reporting settings or not.
controls_.report_settings_checkbox = new views::Checkbox(
l10n_util::GetStringUTF16(IDS_REPORT_BUBBLE_TEXT));
controls_.report_settings_checkbox->SetTextColor(
views::Button::STATE_NORMAL, SK_ColorGRAY);
controls_.report_settings_checkbox->SetChecked(report_checked);
controls_.report_settings_checkbox->SetTextMultiLine(true);
controls_.report_settings_checkbox->set_background(
views::Background::CreateSolidBackground(kLightGrayBackgroundColor));
// Have a smaller margin on the right, to have the |controls_.help_button|
// closer to the edge.
controls_.report_settings_checkbox->set_border(
views::Border::CreateSolidSidedBorder(
kMarginWidth, kMarginWidth, kMarginWidth, kMarginWidth / 2,
kLightGrayBackgroundColor));
// Help button to toggle the bottom panel on or off.
controls_.help_button = new views::ImageButton(this);
const gfx::ImageSkia* help_image = rb.GetImageSkiaNamed(IDR_QUESTION_MARK);
color_utils::HSL hsl_shift = { -1, 0, 0.8 };
brighter_help_image_ = gfx::ImageSkiaOperations::CreateHSLShiftedImage(
*help_image, hsl_shift);
controls_.help_button->SetImage(
views::Button::STATE_NORMAL, &brighter_help_image_);
controls_.help_button->SetImage(views::Button::STATE_HOVERED, help_image);
controls_.help_button->SetImage(views::Button::STATE_PRESSED, help_image);
controls_.help_button->set_background(
views::Background::CreateSolidBackground(kLightGrayBackgroundColor));
controls_.help_button->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
views::ImageButton::ALIGN_MIDDLE);
GridLayout* layout = new GridLayout(this);
SetLayoutManager(layout);
// Title row.
const int kTitleColumnSetId = 0;
views::ColumnSet* cs = layout->AddColumnSet(kTitleColumnSetId);
cs->AddPaddingColumn(0, kMarginWidth);
cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
GridLayout::USE_PREF, 0, 0);
cs->AddPaddingColumn(0, kMarginWidth);
// Text row.
const int kTextColumnSetId = 1;
cs = layout->AddColumnSet(kTextColumnSetId);
cs->AddPaddingColumn(0, kMarginWidth);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::FIXED, kWidthOfDescriptionText, 0);
cs->AddPaddingColumn(0, kMarginWidth);
// Learn more link & buttons row.
const int kButtonsColumnSetId = 2;
cs = layout->AddColumnSet(kButtonsColumnSetId);
cs->AddPaddingColumn(0, kMarginWidth);
cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
GridLayout::USE_PREF, 0, 0);
cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
GridLayout::USE_PREF, 0, 0);
cs->AddPaddingColumn(0, kButtonPadding);
cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
GridLayout::USE_PREF, 0, 0);
cs->AddPaddingColumn(0, kMarginWidth);
// Separator.
const int kSeparatorColumnSetId = 3;
cs = layout->AddColumnSet(kSeparatorColumnSetId);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::FIXED, kAllColumnsWidth, 0);
// Reporting row.
const int kReportColumnSetId = 4;
cs = layout->AddColumnSet(kReportColumnSetId);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::FIXED, kReportingCheckboxColumnWidth, 0);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
GridLayout::FIXED, kHelpButtonColumnWidth, 0);
layout->StartRow(0, kTitleColumnSetId);
layout->AddView(title_label);
layout->AddPaddingRow(0, kMarginHeight);
layout->StartRow(0, kTextColumnSetId);
layout->AddView(text_label);
layout->AddPaddingRow(0, kMarginHeight);
layout->StartRow(0, kButtonsColumnSetId);
layout->AddView(learn_more_link);
layout->AddView(controls_.reset_button);
layout->AddView(controls_.no_thanks_button);
layout->AddPaddingRow(0, kMarginHeight);
layout->StartRow(0, kSeparatorColumnSetId);
layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
layout->StartRow(0, kReportColumnSetId);
layout->AddView(controls_.report_settings_checkbox);
layout->AddView(controls_.help_button);
if (show_help_pane_) {
scoped_ptr<ListValue> feedback(GetReadableFeedback(profile_));
if (feedback.get()) {
// We need a single row to add the scroll view containing the feedback.
const int kReportDetailsColumnSetId = 5;
cs = layout->AddColumnSet(kReportDetailsColumnSetId);
cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
GridLayout::USE_PREF, 0, 0);
FeedbackView* feedback_view = new FeedbackView();
feedback_view->SetupLayoutManager(*feedback.get());
views::ScrollView* scroll_view = new views::ScrollView();
scroll_view->set_background(views::Background::CreateSolidBackground(
kLightGrayBackgroundColor));
scroll_view->SetContents(feedback_view);
layout->StartRow(1, kReportDetailsColumnSetId);
layout->AddView(scroll_view, 1, 1, GridLayout::FILL,
GridLayout::FILL, kAllColumnsWidth,
std::min(feedback_view->height() + kMarginHeight,
kMaxFeedbackViewHeight));
}
}
Layout();
AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
}
void ProfileResetBubbleView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (sender == controls_.reset_button) {
DCHECK(!resetting_);
content::RecordAction(content::UserMetricsAction(
"SettingsResetBubble.Reset"));
// Remember that the user chose to reset, and that resetting is underway.
chose_to_reset_ = true;
resetting_ = true;
controls_.reset_button->SetText(l10n_util::GetStringUTF16(IDS_RESETTING));
controls_.reset_button->SetEnabled(false);
controls_.no_thanks_button->SetEnabled(false);
SchedulePaint();
if (global_error_) {
global_error_->OnBubbleViewResetButtonPressed(
controls_.report_settings_checkbox->checked());
}
} else if (sender == controls_.no_thanks_button) {
DCHECK(!resetting_);
content::RecordAction(content::UserMetricsAction(
"SettingsResetBubble.NoThanks"));
if (global_error_)
global_error_->OnBubbleViewNoThanksButtonPressed();
StartFade(false);
return;
} else if (sender == controls_.help_button) {
show_help_pane_ = !show_help_pane_;
SetupLayoutManager(controls_.report_settings_checkbox->checked());
SizeToContents();
}
}
void ProfileResetBubbleView::LinkClicked(views::Link* source, int flags) {
content::RecordAction(content::UserMetricsAction(
"SettingsResetBubble.LearnMore"));
navigator_->OpenURL(content::OpenURLParams(
GURL(chrome::kResetProfileSettingsLearnMoreURL), content::Referrer(),
NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false));
}
void ProfileResetBubbleView::CloseBubbleView() {
resetting_ = false;
StartFade(false);
}
bool IsProfileResetBubbleSupported() {
return true;
}
GlobalErrorBubbleViewBase* ShowProfileResetBubble(
const base::WeakPtr<ProfileResetGlobalError>& global_error,
Browser* browser) {
return ProfileResetBubbleView::ShowBubble(global_error, browser);
}