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 25using extensions::BundleInstaller; 26 27namespace { 28 29// The horizontal spacing for the main content area. 30const int kHorizontalColumnSpacing = 10; 31 32// The vertical spacing for the text area. 33const int kTextColumnVerticalSpacing = 7; 34 35// The width of the content area. 36const int kContentWidth = 350; 37 38// The padding for list items. 39const int kListItemPadding = 2; 40 41// Padding between content and edge of bubble. 42const int kContentPadding = 12; 43 44} // namespace 45 46// static 47void BundleInstaller::ShowInstalledBubble( 48 const BundleInstaller* bundle, Browser* browser) { 49 new BundleInstalledBubbleGtk(bundle, browser); 50} 51 52BundleInstalledBubbleGtk::BundleInstalledBubbleGtk( 53 const BundleInstaller* bundle, Browser* browser) 54 : browser_(browser), 55 bubble_(NULL) { 56 AddRef(); // Balanced in Close(). 57 ShowInternal(bundle); 58} 59 60BundleInstalledBubbleGtk::~BundleInstalledBubbleGtk() {} 61 62void 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 107void BundleInstalledBubbleGtk::InsertExtensionList( 108 GtkWidget* parent, 109 const BundleInstaller* bundle, 110 BundleInstaller::Item::State state) { 111 base::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 129void 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 138void BundleInstalledBubbleGtk::Close() { 139 bubble_ = NULL; 140 141 Release(); // Balanced in BundleInstalledBubbleGtk(). 142} 143 144void BundleInstalledBubbleGtk::OnButtonClick(GtkWidget* button, 145 BundleInstalledBubbleGtk* bubble) { 146 if (button == bubble->close_button_->widget()) 147 bubble->bubble_->Close(); 148 else 149 NOTREACHED(); 150} 151