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  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