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 "base/i18n/rtl.h" 6 #include "base/strings/utf_string_conversions.h" 7 #include "chrome/browser/extensions/bundle_installer.h" 8 #include "chrome/browser/ui/browser.h" 9 #include "chrome/browser/ui/views/frame/browser_view.h" 10 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" 11 #include "grit/chromium_strings.h" 12 #include "grit/generated_resources.h" 13 #include "grit/theme_resources.h" 14 #include "grit/ui_resources.h" 15 #include "ui/base/l10n/l10n_util.h" 16 #include "ui/base/resource/resource_bundle.h" 17 #include "ui/views/bubble/bubble_delegate.h" 18 #include "ui/views/controls/button/image_button.h" 19 #include "ui/views/controls/image_view.h" 20 #include "ui/views/controls/label.h" 21 #include "ui/views/layout/grid_layout.h" 22 #include "ui/views/layout/layout_constants.h" 23 24 using extensions::BundleInstaller; 25 using views::GridLayout; 26 27 namespace { 28 29 // The ID of the column set for the bubble. 30 const int kColumnSetId = 0; 31 32 // The width of the left column. 33 const int kLeftColumnWidth = 325; 34 35 class BundleInstalledBubble : public views::BubbleDelegateView, 36 public views::ButtonListener { 37 public: 38 BundleInstalledBubble(const BundleInstaller* bundle, 39 View* anchor_view, 40 views::BubbleBorder::Arrow arrow) 41 : views::BubbleDelegateView(anchor_view, arrow) { 42 GridLayout* layout = GridLayout::CreatePanel(this); 43 SetLayoutManager(layout); 44 views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId); 45 46 column_set->AddColumn(GridLayout::LEADING, 47 GridLayout::FILL, 48 0, // no resizing 49 GridLayout::USE_PREF, 50 0, // no fixed with 51 kLeftColumnWidth); 52 column_set->AddPaddingColumn(0, views::kPanelHorizMargin); 53 column_set->AddColumn(GridLayout::LEADING, 54 GridLayout::LEADING, 55 0, // no resizing 56 GridLayout::USE_PREF, 57 0, // no fixed width 58 0); // no min width (only holds close button) 59 60 layout->StartRow(0, kColumnSetId); 61 62 AddContent(layout, bundle); 63 } 64 65 virtual ~BundleInstalledBubble() {} 66 67 private: 68 void AddContent(GridLayout* layout, const BundleInstaller* bundle) { 69 base::string16 installed_heading = bundle->GetHeadingTextFor( 70 BundleInstaller::Item::STATE_INSTALLED); 71 base::string16 failed_heading = bundle->GetHeadingTextFor( 72 BundleInstaller::Item::STATE_FAILED); 73 74 // Insert the list of installed items. 75 if (!installed_heading.empty()) { 76 layout->StartRow(0, kColumnSetId); 77 AddHeading(layout, installed_heading); 78 AddCloseButton(layout, this); 79 AddItemList(layout, bundle->GetItemsWithState( 80 BundleInstaller::Item::STATE_INSTALLED)); 81 82 // Insert a line of padding if we're showing both sections. 83 if (!failed_heading.empty()) 84 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 85 } 86 87 // Insert the list of failed items. 88 if (!failed_heading.empty()) { 89 layout->StartRow(0, kColumnSetId); 90 AddHeading(layout, failed_heading); 91 92 // The close button should be in the second column of the first row, so 93 // we add it here if there was no installed items section. 94 if (installed_heading.empty()) 95 AddCloseButton(layout, this); 96 97 AddItemList(layout, bundle->GetItemsWithState( 98 BundleInstaller::Item::STATE_FAILED)); 99 } 100 101 views::BubbleDelegateView::CreateBubble(this)->Show(); 102 } 103 104 void AddItemList(GridLayout* layout, const BundleInstaller::ItemList& items) { 105 for (size_t i = 0; i < items.size(); ++i) { 106 base::string16 extension_name = 107 base::UTF8ToUTF16(items[i].localized_name); 108 base::i18n::AdjustStringForLocaleDirection(&extension_name); 109 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 110 layout->StartRow(0, kColumnSetId); 111 views::Label* extension_label = new views::Label( 112 l10n_util::GetStringFUTF16( 113 IDS_EXTENSION_PERMISSION_LINE, extension_name)); 114 extension_label->SetMultiLine(true); 115 extension_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 116 extension_label->SizeToFit(kLeftColumnWidth); 117 layout->AddView(extension_label); 118 } 119 } 120 121 void AddCloseButton(GridLayout* layout, views::ButtonListener* listener) { 122 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 123 124 views::ImageButton* button = new views::ImageButton(listener); 125 button->SetImage(views::CustomButton::STATE_NORMAL, 126 rb.GetImageSkiaNamed(IDR_CLOSE_2)); 127 button->SetImage(views::CustomButton::STATE_HOVERED, 128 rb.GetImageSkiaNamed(IDR_CLOSE_2_H)); 129 button->SetImage(views::CustomButton::STATE_PRESSED, 130 rb.GetImageSkiaNamed(IDR_CLOSE_2_P)); 131 layout->AddView(button); 132 } 133 134 void AddHeading(GridLayout* layout, const base::string16& heading) { 135 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 136 views::Label* heading_label = new views::Label( 137 heading, rb.GetFontList(ui::ResourceBundle::MediumFont)); 138 heading_label->SetMultiLine(true); 139 heading_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 140 heading_label->SizeToFit(kLeftColumnWidth); 141 layout->AddView(heading_label); 142 } 143 144 // views::ButtonListener implementation: 145 virtual void ButtonPressed(views::Button* sender, 146 const ui::Event& event) OVERRIDE { 147 GetWidget()->Close(); 148 } 149 150 DISALLOW_COPY_AND_ASSIGN(BundleInstalledBubble); 151 }; 152 153 } // namespace 154 155 void BundleInstaller::ShowInstalledBubble( 156 const BundleInstaller* bundle, Browser* browser) { 157 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); 158 views::View* anchor = browser_view->GetToolbarView()->app_menu(); 159 new BundleInstalledBubble(bundle, anchor, views::BubbleBorder::TOP_RIGHT); 160 } 161