Index: chrome/browser/ui/views/autofill/autofill_dialog_views.cc |
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc |
index cee04ad7a45ba4cba4a6e2282f9507dba92c9e09..d8ddaf7de68617bce676f825b8dc3f95d0e8118b 100644 |
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc |
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc |
@@ -6,6 +6,8 @@ |
#include <utility> |
+#include "base/i18n/break_iterator.h" |
+#include "base/string_util.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/autofill/wallet/wallet_service_url.h" |
#include "chrome/browser/profiles/profile.h" |
@@ -52,6 +54,7 @@ const size_t kArrowWidth = 2 * kArrowHeight; |
const char kDecoratedTextfieldClassName[] = "autofill/DecoratedTextfield"; |
const char kNotificationAreaClassName[] = "autofill/NotificationArea"; |
+const char kFootnoteViewClassName[] = "autofill/FootnoteView"; |
// Returns a label that describes a details section. |
views::Label* CreateDetailsSectionLabel(const string16& text) { |
@@ -332,6 +335,7 @@ void AutofillDialogViews::SuggestionView::ShowTextfield( |
} |
// AutofilDialogViews::AutocheckoutProgressBar --------------------------------- |
+ |
AutofillDialogViews::AutocheckoutProgressBar::AutocheckoutProgressBar() {} |
gfx::Size AutofillDialogViews::AutocheckoutProgressBar::GetPreferredSize() { |
@@ -339,6 +343,209 @@ gfx::Size AutofillDialogViews::AutocheckoutProgressBar::GetPreferredSize() { |
kAutocheckoutProgressBarHeight); |
} |
+// AutofillDialogViews::FootnoteView ------------------------------------------- |
+ |
+AutofillDialogViews::FootnoteView::FootnoteView() |
+ : current_width_(0), |
+ layout_(NULL), |
+ previous_parent_width_(-1), |
+ single_column_set_(0) { |
+ set_border(views::Border::CreateEmptyBorder( |
+ views::kUnrelatedControlVerticalSpacing, 0, |
+ views::kUnrelatedControlVerticalSpacing, 0)); |
+} |
+ |
+AutofillDialogViews::FootnoteView::~FootnoteView() {} |
+ |
+std::string AutofillDialogViews::FootnoteView::GetClassName() const { |
+ return kFootnoteViewClassName; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::AddLabel(views::Label* label, |
+ bool is_link) { |
+ FootnoteLabel* footnote_label = new FootnoteLabel(); |
+ footnote_label->label.reset(label); |
+ footnote_label->is_link = is_link; |
+ footnote_labels_.push_back(footnote_label); |
+ previous_parent_width_ = -1; |
+} |
+ |
+int AutofillDialogViews::FootnoteView::IndexOfLink(views::Label* link) { |
+ size_t distance = std::distance( |
+ links_.begin(), std::find(links_.begin(), links_.end(), link)); |
+ return distance < links_.size() ? distance : -1; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::StartRow() { |
+ layout_->StartRow(0, single_column_set_); |
+ views::View* row = new views::View(); |
+ row->SetLayoutManager( |
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); |
+ layout_->AddView(row); |
+} |
+ |
+views::View* AutofillDialogViews::FootnoteView::CurrentRow() { |
+ return child_at(child_count() - 1); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::ResetRows() { |
+ ResetLayoutManager(); |
+ RemoveAllChildViews(true); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::ResetLayoutManager() { |
+ views::GridLayout* layout = new views::GridLayout(this); |
+ SetLayoutManager(layout); |
+ |
+ layout_ = layout; |
+ layout_->AddColumnSet(single_column_set_)->AddColumn( |
+ views::GridLayout::FILL, |
+ views::GridLayout::FILL, |
+ 1, |
+ views::GridLayout::USE_PREF, |
+ 0, |
+ 0); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::Reset() { |
+ ResetRows(); |
+ ResetText(); |
+ ResetWidth(); |
+ StartRow(); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::AppendText(const string16& text_part) { |
+ current_text_.append(text_part); |
+} |
+ |
+const string16& AutofillDialogViews::FootnoteView::CurrentText() const { |
+ return current_text_; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::ResetWidth() { |
+ current_width_ = 0; |
+} |
+ |
+size_t AutofillDialogViews::FootnoteView::CurrentWidth() const { |
+ return current_width_; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::AddWidth(size_t width) { |
+ current_width_ += width; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::ResetText() { |
+ current_text_.clear(); |
+} |
+ |
+int AutofillDialogViews::FootnoteView::ParentWidth() { |
+ DCHECK(parent()); |
+ return parent()->GetPreferredSize().width(); |
+} |
+ |
+int AutofillDialogViews::FootnoteView::WidthRemaining() { |
+ return ParentWidth() - CurrentWidth(); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::AddCloneWithWidth(views::Label* label, |
+ size_t width) { |
+ AddWidth(width); |
+ CurrentRow()->AddChildView(label); |
+} |
+ |
+views::Label* AutofillDialogViews::FootnoteView::CloneLabel( |
+ const string16& text, const gfx::Font& font, bool is_link) { |
+ views::Label* label = is_link ? new views::Link(text) : |
+ new views::Label(text); |
+ label->SetFont(font); |
+ return label; |
+} |
+ |
+void AutofillDialogViews::FootnoteView::TransferListener( |
+ views::Label* from_label, views::Label* to_label) { |
+ views::Link* from = static_cast<views::Link*>(from_label); |
+ views::Link* to = static_cast<views::Link*>(to_label); |
+ to->set_listener(const_cast<views::LinkListener*>(from->listener())); |
+} |
+ |
+void AutofillDialogViews::FootnoteView::Layout() { |
+ views::View::Layout(); |
+ |
+ if (!parent() || previous_parent_width_ == ParentWidth()) |
+ return; |
+ |
+ Reset(); |
+ |
+ for (size_t i = 0; i < footnote_labels_.size(); ++i) { |
+ views::Label* label = footnote_labels_[i]->label.get(); |
+ if (!label->visible()) |
+ continue; |
+ |
+ // The label is smaller than the available space. Add it wholesale. |
+ int tw = gfx::Canvas::GetStringWidth(label->text(), label->font()); |
+ if (tw < WidthRemaining()) { |
+ views::Label* clone = CloneLabel(label->text(), |
+ label->font(), |
+ footnote_labels_[i]->is_link); |
+ if (footnote_labels_[i]->is_link) { |
+ TransferListener(label, clone); |
+ links_.push_back(clone); |
+ } |
+ AddCloneWithWidth(clone, tw); |
+ continue; |
+ } |
+ |
+ // Don't wrap links mid-line. This one's too big, so make a new row. |
+ if (footnote_labels_[i]->is_link) { |
+ StartRow(); |
+ ResetWidth(); |
+ links_.push_back(CloneLabel(label->text(), label->font(), true)); |
+ TransferListener(label, links_.back()); |
+ AddCloneWithWidth(links_.back(), tw); |
+ if (tw > ParentWidth()) { |
+ StartRow(); |
+ ResetWidth(); |
+ } |
+ continue; |
+ } |
+ |
+ // Go through the text word by word, seeing if each one fits. |
+ DCHECK(CurrentText().empty()); |
+ base::i18n::BreakIterator iter(label->text(), |
+ base::i18n::BreakIterator::BREAK_SPACE); |
+ for (bool first_word = true, more_words = iter.Init() && iter.Advance(); |
+ more_words; ) { |
+ int width = 0; |
+ // Iterate until no more words or space left. |
+ do { |
+ string16 text = iter.GetString(); |
+ if (first_word) { |
+ TrimWhitespace(text, TRIM_LEADING, &text); |
+ first_word = false; |
+ } |
+ |
+ const int w = gfx::Canvas::GetStringWidth(text, label->font()); |
+ if (width + w > WidthRemaining()) |
+ break; |
+ |
+ width += w; |
+ AppendText(text); |
+ } while ((more_words = iter.Advance())); |
+ |
+ // If we didn't run out of words, the space is full. Start a new row. |
+ if (more_words) { |
+ StartRow(); |
+ ResetWidth(); |
+ } |
+ |
+ AddCloneWithWidth(CloneLabel(CurrentText(), label->font(), false), width); |
+ ResetText(); |
+ } |
+ } |
+ |
+ previous_parent_width_ = ParentWidth(); |
+} |
+ |
// AutofillDialogView ---------------------------------------------------------- |
// static |
@@ -365,6 +572,7 @@ AutofillDialogViews::AutofillDialogViews(AutofillDialogController* controller) |
save_in_chrome_checkbox_(NULL), |
autocheckout_progress_bar_view_(NULL), |
autocheckout_progress_bar_(NULL), |
+ footnote_view_(NULL), |
focus_manager_(NULL) { |
DCHECK(controller); |
detail_groups_.insert(std::make_pair(SECTION_EMAIL, |
@@ -389,6 +597,7 @@ void AutofillDialogViews::Show() { |
InitChildViews(); |
UpdateAccountChooser(); |
UpdateNotificationArea(); |
+ UpdateFootnote(); |
// Ownership of |contents_| is handed off by this call. The |
// WebContentsModalDialog will take care of deleting itself after calling |
@@ -413,11 +622,7 @@ void AutofillDialogViews::UpdateAccountChooser() { |
void AutofillDialogViews::UpdateNotificationArea() { |
DCHECK(notification_area_); |
notification_area_->SetNotification(controller_->CurrentNotification()); |
- |
- if (GetWidget()) |
- GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize()); |
- |
- contents_->Layout(); |
+ ContentsResized(); |
} |
void AutofillDialogViews::UpdateSection(DialogSection section) { |
@@ -484,6 +689,11 @@ void AutofillDialogViews::UpdateProgressBar(double value) { |
autocheckout_progress_bar_->SetValue(value); |
} |
+void AutofillDialogViews::UpdateFootnote() { |
+ PopulateFootnoteLinks(); |
+ ContentsResized(); |
+} |
+ |
string16 AutofillDialogViews::GetWindowTitle() const { |
return controller_->DialogTitle(); |
} |
@@ -525,8 +735,7 @@ views::View* AutofillDialogViews::GetExtraView() { |
} |
views::View* AutofillDialogViews::GetFootnoteView() { |
- // TODO(estade): add a view to contain the terms of service. |
- return NULL; |
+ return footnote_view_; |
} |
bool AutofillDialogViews::Cancel() { |
@@ -607,22 +816,22 @@ void AutofillDialogViews::OnDidChangeFocus( |
views::View* focused_now) {} |
void AutofillDialogViews::LinkClicked(views::Link* source, int event_flags) { |
- // Sign in link. |
if (source == account_chooser_link_) { |
if (controller_->SignedInState() != SIGNED_IN) { |
DCHECK(controller_->CanPayWithWallet()); |
controller_->StartSignInFlow(); |
} |
// TODO(dbeam): handle other clicks on the account chooser (i.e. combobox). |
- return; |
- } |
- |
- // Edit links. |
- for (DetailGroupMap::iterator iter = detail_groups_.begin(); |
- iter != detail_groups_.end(); ++iter) { |
- if (iter->second.suggested_info->Contains(source)) { |
- controller_->EditClickedForSection(iter->first); |
- return; |
+ } else if (footnote_view_->Contains(source)) { |
+ controller_->LegalDocumentLinkClicked(footnote_view_->IndexOfLink(source)); |
+ } else { |
+ // Edit links. |
+ for (DetailGroupMap::iterator iter = detail_groups_.begin(); |
+ iter != detail_groups_.end(); ++iter) { |
+ if (iter->second.suggested_info->Contains(source)) { |
+ controller_->EditClickedForSection(iter->first); |
+ return; |
+ } |
} |
} |
} |
@@ -656,19 +865,9 @@ void AutofillDialogViews::InitChildViews() { |
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); |
contents_->AddChildView(CreateMainContainer()); |
contents_->AddChildView(CreateSignInContainer()); |
-} |
-views::View* AutofillDialogViews::CreateSignInContainer() { |
- sign_in_container_ = new views::View(); |
- sign_in_container_->SetLayoutManager( |
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); |
- sign_in_container_->SetVisible(false); |
- sign_in_webview_ = new views::WebView(controller_->profile()); |
- cancel_sign_in_ = new views::TextButton(this, |
- controller_->CancelSignInText()); |
- sign_in_container_->AddChildView(cancel_sign_in_); |
- sign_in_container_->AddChildView(sign_in_webview_); |
- return sign_in_container_; |
+ // |footnote_view_| is added to View hierarchy by |GetFootnoteView()|. |
+ footnote_view_ = new FootnoteView(); |
} |
views::View* AutofillDialogViews::CreateMainContainer() { |
@@ -705,6 +904,37 @@ views::View* AutofillDialogViews::CreateMainContainer() { |
return main_container_; |
} |
+views::View* AutofillDialogViews::CreateSignInContainer() { |
+ sign_in_container_ = new views::View(); |
+ sign_in_container_->SetLayoutManager( |
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); |
+ sign_in_container_->SetVisible(false); |
+ sign_in_webview_ = new views::WebView(controller_->profile()); |
+ cancel_sign_in_ = new views::TextButton(this, |
+ controller_->CancelSignInText()); |
+ sign_in_container_->AddChildView(cancel_sign_in_); |
+ sign_in_container_->AddChildView(sign_in_webview_); |
+ return sign_in_container_; |
+} |
+ |
+void AutofillDialogViews::PopulateFootnoteLinks() { |
+ const std::vector<string16>& link_parts = controller_->FootnoteLinkParts(); |
+ for (size_t i = 0; i < link_parts.size(); ++i) { |
+ if (i % 2 == 0) { |
+ // Text between links. |
+ footnote_view_->AddLabel(new views::Label(link_parts[i]), false); |
+ } else { |
+ // Link to a legal document (i.e. Terms Of Service, Privacy Policy). |
+ views::Link* link = new views::Link(link_parts[i]); |
+ link->SetEnabledColor(SkColorSetRGB(0x64, 0x64, 0x64)); |
+ link->set_listener(this); |
+ footnote_view_->AddLabel(link, true); |
+ } |
+ } |
+ footnote_view_->Layout(); |
+ ContentsResized(); |
+} |
+ |
views::View* AutofillDialogViews::CreateDetailsContainer() { |
views::View* view = new views::View(); |
// A box layout is used because it respects widget visibility. |
@@ -738,7 +968,7 @@ views::View* AutofillDialogViews::CreateInputsContainer(DialogSection section) { |
views::GridLayout* layout = new views::GridLayout(inputs_container); |
inputs_container->SetLayoutManager(layout); |
- int kColumnSetId = 0; |
+ const int kColumnSetId = 0; |
views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId); |
column_set->AddColumn(views::GridLayout::FILL, |
views::GridLayout::LEADING, |
@@ -892,8 +1122,7 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) { |
if (group.container) |
group.container->SetForwardMouseEvents(show_suggestions); |
- if (GetWidget()) |
- GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize()); |
+ ContentsResized(); |
} |
bool AutofillDialogViews::AtLeastOneSectionIsEditing() { |
@@ -981,6 +1210,11 @@ void AutofillDialogViews::TextfieldEditedOrActivated( |
decorated->SetInvalid(!controller_->InputIsValid(type, textfield->text())); |
} |
+void AutofillDialogViews::ContentsResized() { |
+ if (GetWidget()) |
+ GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize()); |
+} |
+ |
AutofillDialogViews::DetailsGroup* AutofillDialogViews::GroupForSection( |
DialogSection section) { |
return &detail_groups_.find(section)->second; |