global_error_bubble_view.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/global_error_bubble_view.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/ui/global_error/global_error.h"
9#include "chrome/browser/ui/global_error/global_error_service.h"
10#include "chrome/browser/ui/global_error/global_error_service_factory.h"
11#include "chrome/browser/ui/views/frame/browser_view.h"
12#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
13#include "ui/base/resource/resource_bundle.h"
14#include "ui/gfx/image/image.h"
15#include "ui/views/controls/button/label_button.h"
16#include "ui/views/controls/image_view.h"
17#include "ui/views/controls/label.h"
18#include "ui/views/layout/grid_layout.h"
19#include "ui/views/layout/layout_constants.h"
20
21namespace {
22
23enum {
24  TAG_ACCEPT_BUTTON = 1,
25  TAG_CANCEL_BUTTON,
26};
27
28const int kMaxBubbleViewWidth = 262;
29
30// The horizontal padding between the title and the icon.
31const int kTitleHorizontalPadding = 5;
32
33// The vertical inset of the wrench bubble anchor from the wrench menu button.
34const int kAnchorVerticalInset = 5;
35
36const int kBubblePadding = 6;
37
38}  // namespace
39
40// GlobalErrorBubbleViewBase ---------------------------------------------------
41
42// static
43GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
44    Browser* browser,
45    const base::WeakPtr<GlobalErrorWithStandardBubble>& error) {
46  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
47  views::View* wrench_button = browser_view->toolbar()->app_menu();
48  GlobalErrorBubbleView* bubble_view =
49      new GlobalErrorBubbleView(wrench_button,
50                                views::BubbleBorder::TOP_RIGHT,
51                                browser,
52                                error);
53  views::BubbleDelegateView::CreateBubble(bubble_view);
54  bubble_view->StartFade(true);
55  return bubble_view;
56}
57
58// GlobalErrorBubbleView -------------------------------------------------------
59
60GlobalErrorBubbleView::GlobalErrorBubbleView(
61    views::View* anchor_view,
62    views::BubbleBorder::Arrow arrow,
63    Browser* browser,
64    const base::WeakPtr<GlobalErrorWithStandardBubble>& error)
65    : BubbleDelegateView(anchor_view, arrow),
66      browser_(browser),
67      error_(error) {
68  // Compensate for built-in vertical padding in the anchor view's image.
69  set_anchor_view_insets(
70      gfx::Insets(kAnchorVerticalInset, 0, kAnchorVerticalInset, 0));
71
72  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
73  gfx::Image image = error_->GetBubbleViewIcon();
74  CHECK(!image.IsEmpty());
75  scoped_ptr<views::ImageView> image_view(new views::ImageView());
76  image_view->SetImage(image.ToImageSkia());
77
78  base::string16 title_string(error_->GetBubbleViewTitle());
79  scoped_ptr<views::Label> title_label(new views::Label(title_string));
80  title_label->SetMultiLine(true);
81  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
82  title_label->SetFont(rb.GetFont(ui::ResourceBundle::MediumFont));
83
84  std::vector<string16> message_strings(error_->GetBubbleViewMessages());
85  std::vector<views::Label*> message_labels;
86  for (size_t i = 0; i < message_strings.size(); ++i) {
87    views::Label* message_label = new views::Label(message_strings[i]);
88    message_label->SetMultiLine(true);
89    message_label->SizeToFit(kMaxBubbleViewWidth);
90    message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
91    message_labels.push_back(message_label);
92  }
93
94  base::string16 accept_string(error_->GetBubbleViewAcceptButtonLabel());
95  scoped_ptr<views::LabelButton> accept_button(
96      new views::LabelButton(this, accept_string));
97  accept_button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
98  accept_button->SetIsDefault(true);
99  accept_button->set_tag(TAG_ACCEPT_BUTTON);
100
101  base::string16 cancel_string(error_->GetBubbleViewCancelButtonLabel());
102  scoped_ptr<views::LabelButton> cancel_button;
103  if (!cancel_string.empty()) {
104    cancel_button.reset(new views::LabelButton(this, cancel_string));
105    cancel_button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
106    cancel_button->set_tag(TAG_CANCEL_BUTTON);
107  }
108
109  views::GridLayout* layout = new views::GridLayout(this);
110  SetLayoutManager(layout);
111  layout->SetInsets(kBubblePadding, kBubblePadding,
112                    kBubblePadding, kBubblePadding);
113
114  // Top row, icon and title.
115  views::ColumnSet* cs = layout->AddColumnSet(0);
116  cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
117                0, views::GridLayout::USE_PREF, 0, 0);
118  cs->AddPaddingColumn(0, kTitleHorizontalPadding);
119  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
120                1, views::GridLayout::USE_PREF, 0, 0);
121
122  // Middle rows, message labels.
123  cs = layout->AddColumnSet(1);
124  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
125                1, views::GridLayout::USE_PREF, 0, 0);
126
127  // Bottom row, accept and cancel button.
128  cs = layout->AddColumnSet(2);
129  cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
130  cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING,
131                0, views::GridLayout::USE_PREF, 0, 0);
132  if (cancel_button.get()) {
133    cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
134    cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING,
135                  0, views::GridLayout::USE_PREF, 0, 0);
136  }
137
138  layout->StartRow(1, 0);
139  layout->AddView(image_view.release());
140  layout->AddView(title_label.release());
141  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
142
143  for (size_t i = 0; i < message_labels.size(); ++i) {
144    layout->StartRow(1, 1);
145    layout->AddView(message_labels[i]);
146    if (i < message_labels.size() - 1)
147      layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
148  }
149  layout->AddPaddingRow(0, views::kLabelToControlVerticalSpacing);
150
151  layout->StartRow(0, 2);
152  layout->AddView(accept_button.release());
153  if (cancel_button.get())
154    layout->AddView(cancel_button.release());
155
156  // Adjust the message label size in case buttons are too long.
157  for (size_t i = 0; i < message_labels.size(); ++i)
158    message_labels[i]->SizeToFit(layout->GetPreferredSize(this).width());
159}
160
161GlobalErrorBubbleView::~GlobalErrorBubbleView() {
162}
163
164void GlobalErrorBubbleView::ButtonPressed(views::Button* sender,
165                                          const ui::Event& event) {
166  if (error_) {
167    if (sender->tag() == TAG_ACCEPT_BUTTON)
168      error_->BubbleViewAcceptButtonPressed(browser_);
169    else if (sender->tag() == TAG_CANCEL_BUTTON)
170      error_->BubbleViewCancelButtonPressed(browser_);
171    else
172      NOTREACHED();
173  }
174  GetWidget()->Close();
175}
176
177void GlobalErrorBubbleView::WindowClosing() {
178  if (error_)
179    error_->BubbleViewDidClose(browser_);
180}
181
182void GlobalErrorBubbleView::CloseBubbleView() {
183  GetWidget()->Close();
184}
185