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/gtk/extensions/bundle_installed_bubble_gtk.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/i18n/rtl.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h" 16 #include "chrome/browser/ui/gtk/browser_window_gtk.h" 17 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 18 #include "chrome/browser/ui/gtk/gtk_util.h" 19 #include "grit/generated_resources.h" 20 #include "grit/theme_resources.h" 21 #include "ui/base/l10n/l10n_util.h" 22 #include "ui/base/resource/resource_bundle.h" 23 #include "ui/gfx/gtk_util.h" 24 25 using extensions::BundleInstaller; 26 27 namespace { 28 29 // The horizontal spacing for the main content area. 30 const int kHorizontalColumnSpacing = 10; 31 32 // The vertical spacing for the text area. 33 const int kTextColumnVerticalSpacing = 7; 34 35 // The width of the content area. 36 const int kContentWidth = 350; 37 38 // The padding for list items. 39 const int kListItemPadding = 2; 40 41 // Padding between content and edge of bubble. 42 const int kContentPadding = 12; 43 44 } // namespace 45 46 // static 47 void BundleInstaller::ShowInstalledBubble( 48 const BundleInstaller* bundle, Browser* browser) { 49 new BundleInstalledBubbleGtk(bundle, browser); 50 } 51 52 BundleInstalledBubbleGtk::BundleInstalledBubbleGtk( 53 const BundleInstaller* bundle, Browser* browser) 54 : browser_(browser), 55 bubble_(NULL) { 56 AddRef(); // Balanced in Close(). 57 ShowInternal(bundle); 58 } 59 60 BundleInstalledBubbleGtk::~BundleInstalledBubbleGtk() {} 61 62 void BundleInstalledBubbleGtk::ShowInternal(const BundleInstaller* bundle) { 63 BrowserWindowGtk* browser_window = 64 BrowserWindowGtk::GetBrowserWindowForNativeWindow( 65 browser_->window()->GetNativeWindow()); 66 67 GtkThemeService* theme_provider = GtkThemeService::GetFrom( 68 browser_->profile()); 69 70 // Anchor the bubble to the wrench menu. 71 GtkWidget* reference_widget = 72 browser_window->GetToolbar()->GetAppMenuButton(); 73 74 GtkWidget* bubble_content = gtk_hbox_new(FALSE, kHorizontalColumnSpacing); 75 gtk_container_set_border_width( 76 GTK_CONTAINER(bubble_content), kContentPadding); 77 78 GtkWidget* text_column = gtk_vbox_new(FALSE, kTextColumnVerticalSpacing); 79 gtk_box_pack_start(GTK_BOX(bubble_content), text_column, FALSE, FALSE, 0); 80 81 InsertExtensionList( 82 text_column, bundle, BundleInstaller::Item::STATE_INSTALLED); 83 InsertExtensionList(text_column, bundle, BundleInstaller::Item::STATE_FAILED); 84 85 // Close button 86 GtkWidget* close_column = gtk_vbox_new(FALSE, 0); 87 gtk_box_pack_start(GTK_BOX(bubble_content), close_column, FALSE, FALSE, 0); 88 close_button_.reset(CustomDrawButton::CloseButtonBubble(theme_provider)); 89 g_signal_connect(close_button_->widget(), "clicked", 90 G_CALLBACK(OnButtonClick), this); 91 gtk_box_pack_start(GTK_BOX(close_column), close_button_->widget(), 92 FALSE, FALSE, 0); 93 94 gfx::Rect bounds = gtk_util::WidgetBounds(reference_widget); 95 96 bubble_ = BubbleGtk::Show(reference_widget, 97 &bounds, 98 bubble_content, 99 BubbleGtk::ANCHOR_TOP_RIGHT, 100 BubbleGtk::MATCH_SYSTEM_THEME | 101 BubbleGtk::POPUP_WINDOW | 102 BubbleGtk::GRAB_INPUT, 103 theme_provider, 104 this); 105 } 106 107 void BundleInstalledBubbleGtk::InsertExtensionList( 108 GtkWidget* parent, 109 const BundleInstaller* bundle, 110 BundleInstaller::Item::State state) { 111 string16 heading = bundle->GetHeadingTextFor(state); 112 BundleInstaller::ItemList items = bundle->GetItemsWithState(state); 113 if (heading.empty() || items.empty()) 114 return; 115 116 GtkWidget* heading_label = gtk_util::CreateBoldLabel(UTF16ToUTF8(heading)); 117 gtk_util::SetLabelWidth(heading_label, kContentWidth); 118 gtk_box_pack_start(GTK_BOX(parent), heading_label, FALSE, FALSE, 0); 119 120 for (size_t i = 0; i < items.size(); ++i) { 121 GtkWidget* extension_label = gtk_label_new(UTF16ToUTF8( 122 items[i].GetNameForDisplay()).c_str()); 123 gtk_util::SetLabelWidth(extension_label, kContentWidth); 124 gtk_box_pack_start(GTK_BOX(parent), extension_label, false, false, 125 kListItemPadding); 126 } 127 } 128 129 void BundleInstalledBubbleGtk::BubbleClosing(BubbleGtk* bubble, 130 bool closed_by_escape) { 131 // We need to allow the bubble to close and remove the widgets from 132 // the window before we call Release() because close_button_ depends 133 // on all references being cleared before it is destroyed. 134 base::MessageLoopForUI::current()->PostTask( 135 FROM_HERE, base::Bind(&BundleInstalledBubbleGtk::Close, this)); 136 } 137 138 void BundleInstalledBubbleGtk::Close() { 139 bubble_ = NULL; 140 141 Release(); // Balanced in BundleInstalledBubbleGtk(). 142 } 143 144 void BundleInstalledBubbleGtk::OnButtonClick(GtkWidget* button, 145 BundleInstalledBubbleGtk* bubble) { 146 if (button == bubble->close_button_->widget()) 147 bubble->bubble_->Close(); 148 else 149 NOTREACHED(); 150 } 151