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.
5 #include "chrome/browser/ui/views/global_error_bubble_view.h"
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"
24 TAG_ACCEPT_BUTTON
= 1,
28 const int kMaxBubbleViewWidth
= 262;
30 // The horizontal padding between the title and the icon.
31 const int kTitleHorizontalPadding
= 5;
33 // The vertical inset of the wrench bubble anchor from the wrench menu button.
34 const int kAnchorVerticalInset
= 5;
36 const int kBubblePadding
= 6;
40 // GlobalErrorBubbleViewBase ---------------------------------------------------
43 GlobalErrorBubbleViewBase
* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
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
,
53 views::BubbleDelegateView::CreateBubble(bubble_view
);
54 bubble_view
->GetWidget()->Show();
58 // GlobalErrorBubbleView -------------------------------------------------------
60 GlobalErrorBubbleView::GlobalErrorBubbleView(
61 views::View
* anchor_view
,
62 views::BubbleBorder::Arrow arrow
,
64 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
)
65 : BubbleDelegateView(anchor_view
, arrow
),
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));
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());
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
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
84 std::vector
<base::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
);
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_BUTTON
);
98 accept_button
->SetIsDefault(true);
99 accept_button
->set_tag(TAG_ACCEPT_BUTTON
);
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_BUTTON
);
106 cancel_button
->set_tag(TAG_CANCEL_BUTTON
);
109 views::GridLayout
* layout
= new views::GridLayout(this);
110 SetLayoutManager(layout
);
111 layout
->SetInsets(kBubblePadding
, kBubblePadding
,
112 kBubblePadding
, kBubblePadding
);
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);
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);
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);
138 layout
->StartRow(1, 0);
139 layout
->AddView(image_view
.release());
140 layout
->AddView(title_label
.release());
141 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
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
);
149 layout
->AddPaddingRow(0, views::kLabelToControlVerticalSpacing
);
151 layout
->StartRow(0, 2);
152 layout
->AddView(accept_button
.release());
153 if (cancel_button
.get())
154 layout
->AddView(cancel_button
.release());
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());
160 // These bubbles show at times where activation is sporadic (like at startup,
161 // or a new window opening). Make sure the bubble doesn't disappear before the
163 set_close_on_deactivate(false);
166 GlobalErrorBubbleView::~GlobalErrorBubbleView() {
169 void GlobalErrorBubbleView::ButtonPressed(views::Button
* sender
,
170 const ui::Event
& event
) {
172 if (sender
->tag() == TAG_ACCEPT_BUTTON
)
173 error_
->BubbleViewAcceptButtonPressed(browser_
);
174 else if (sender
->tag() == TAG_CANCEL_BUTTON
)
175 error_
->BubbleViewCancelButtonPressed(browser_
);
179 GetWidget()->Close();
182 void GlobalErrorBubbleView::WindowClosing() {
184 error_
->BubbleViewDidClose(browser_
);
187 void GlobalErrorBubbleView::CloseBubbleView() {
188 GetWidget()->Close();