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