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 21 namespace { 22 23 enum { 24 TAG_ACCEPT_BUTTON = 1, 25 TAG_CANCEL_BUTTON, 26 }; 27 28 const int kMaxBubbleViewWidth = 262; 29 30 // The horizontal padding between the title and the icon. 31 const int kTitleHorizontalPadding = 5; 32 33 // The vertical inset of the wrench bubble anchor from the wrench menu button. 34 const int kAnchorVerticalInset = 5; 35 36 const int kBubblePadding = 6; 37 38 } // namespace 39 40 // GlobalErrorBubbleViewBase --------------------------------------------------- 41 42 // static 43 GlobalErrorBubbleViewBase* 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->GetWidget()->Show(); 55 return bubble_view; 56 } 57 58 // GlobalErrorBubbleView ------------------------------------------------------- 59 60 GlobalErrorBubbleView::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->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont)); 83 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); 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_BUTTON); 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_BUTTON); 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 // 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 162 // user sees it, if the bubble needs to be acknowledged. 163 set_close_on_deactivate(error_->ShouldCloseOnDeactivate()); 164 } 165 166 GlobalErrorBubbleView::~GlobalErrorBubbleView() { 167 } 168 169 void GlobalErrorBubbleView::ButtonPressed(views::Button* sender, 170 const ui::Event& event) { 171 if (error_) { 172 if (sender->tag() == TAG_ACCEPT_BUTTON) 173 error_->BubbleViewAcceptButtonPressed(browser_); 174 else if (sender->tag() == TAG_CANCEL_BUTTON) 175 error_->BubbleViewCancelButtonPressed(browser_); 176 else 177 NOTREACHED(); 178 } 179 GetWidget()->Close(); 180 } 181 182 void GlobalErrorBubbleView::WindowClosing() { 183 if (error_) 184 error_->BubbleViewDidClose(browser_); 185 } 186 187 void GlobalErrorBubbleView::CloseBubbleView() { 188 GetWidget()->Close(); 189 } 190