1// Copyright (c) 2011 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 <gtk/gtk.h>
6
7#include "base/string_util.h"
8#include "base/utf_string_conversions.h"
9#include "chrome/browser/extensions/extension_install_dialog.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/ui/browser_list.h"
12#include "chrome/browser/ui/browser_window.h"
13#include "chrome/browser/ui/gtk/browser_window_gtk.h"
14#include "chrome/browser/ui/gtk/gtk_util.h"
15#include "chrome/common/extensions/extension.h"
16#include "grit/generated_resources.h"
17#include "skia/ext/image_operations.h"
18#include "ui/base/l10n/l10n_util.h"
19#include "ui/gfx/gtk_util.h"
20
21namespace {
22
23const int kRightColumnMinWidth = 290;
24const int kImageSize = 69;
25
26// Padding on all sides of each permission in the permissions list.
27const int kPermissionsPadding = 8;
28
29// Make a GtkLabel with |str| as its text, using the formatting in |format|.
30GtkWidget* MakeMarkupLabel(const char* format, const std::string& str) {
31  GtkWidget* label = gtk_label_new(NULL);
32  char* markup = g_markup_printf_escaped(format, str.c_str());
33  gtk_label_set_markup(GTK_LABEL(label), markup);
34  g_free(markup);
35
36  // Left align it.
37  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
38
39  return label;
40}
41
42void OnResponse(GtkWidget* dialog, int response_id,
43                ExtensionInstallUI::Delegate* delegate) {
44  if (response_id == GTK_RESPONSE_ACCEPT) {
45    delegate->InstallUIProceed();
46  } else {
47    delegate->InstallUIAbort();
48  }
49
50  gtk_widget_destroy(dialog);
51}
52
53void ShowInstallDialog(GtkWindow* parent,
54                       SkBitmap* skia_icon,
55                       const Extension* extension,
56                       ExtensionInstallUI::Delegate *delegate,
57                       const std::vector<string16>& permissions,
58                       ExtensionInstallUI::PromptType type) {
59  // Build the dialog.
60  GtkWidget* dialog = gtk_dialog_new_with_buttons(
61      l10n_util::GetStringUTF8(ExtensionInstallUI::kTitleIds[type]).c_str(),
62      parent,
63      GTK_DIALOG_MODAL,
64      NULL);
65  GtkWidget* close_button = gtk_dialog_add_button(GTK_DIALOG(dialog),
66      GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE);
67  gtk_dialog_add_button(
68      GTK_DIALOG(dialog),
69      l10n_util::GetStringUTF8(ExtensionInstallUI::kButtonIds[type]).c_str(),
70      GTK_RESPONSE_ACCEPT);
71  gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
72
73  // Create a two column layout.
74  GtkWidget* content_area = GTK_DIALOG(dialog)->vbox;
75  gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
76
77  GtkWidget* icon_hbox = gtk_hbox_new(FALSE, gtk_util::kContentAreaSpacing);
78  gtk_box_pack_start(GTK_BOX(content_area), icon_hbox, TRUE, TRUE, 0);
79
80  // Resize the icon if necessary.
81  SkBitmap scaled_icon = *skia_icon;
82  if (scaled_icon.width() > kImageSize || scaled_icon.height() > kImageSize) {
83    scaled_icon = skia::ImageOperations::Resize(scaled_icon,
84        skia::ImageOperations::RESIZE_LANCZOS3,
85        kImageSize, kImageSize);
86  }
87
88  // Put Icon in the left column.
89  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&scaled_icon);
90  GtkWidget* icon = gtk_image_new_from_pixbuf(pixbuf);
91  g_object_unref(pixbuf);
92  gtk_box_pack_start(GTK_BOX(icon_hbox), icon, FALSE, FALSE, 0);
93  // Top justify the image.
94  gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0.0);
95
96  // Create a new vbox for the right column.
97  GtkWidget* right_column_area = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
98  gtk_box_pack_start(GTK_BOX(icon_hbox), right_column_area, TRUE, TRUE, 0);
99
100  std::string heading_text = l10n_util::GetStringFUTF8(
101      ExtensionInstallUI::kHeadingIds[type], UTF8ToUTF16(extension->name()));
102  GtkWidget* heading_label = MakeMarkupLabel("<span weight=\"bold\">%s</span>",
103                                             heading_text);
104  gtk_label_set_line_wrap(GTK_LABEL(heading_label), true);
105  gtk_misc_set_alignment(GTK_MISC(heading_label), 0.0, 0.5);
106  bool show_permissions = !permissions.empty();
107  // If we are not going to show the permissions, vertically center the title.
108  gtk_box_pack_start(GTK_BOX(right_column_area), heading_label,
109                     !show_permissions, !show_permissions, 0);
110
111  if (show_permissions) {
112    GtkWidget* warning_label = gtk_label_new(l10n_util::GetStringUTF8(
113        ExtensionInstallUI::kWarningIds[type]).c_str());
114    gtk_util::SetLabelWidth(warning_label, kRightColumnMinWidth);
115
116    gtk_box_pack_start(GTK_BOX(right_column_area), warning_label,
117                       FALSE, FALSE, 0);
118
119    GtkWidget* frame = gtk_frame_new(NULL);
120    gtk_box_pack_start(GTK_BOX(right_column_area), frame, FALSE, FALSE, 0);
121
122    GtkWidget* text_view = gtk_text_view_new();
123    gtk_container_add(GTK_CONTAINER(frame), text_view);
124    gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
125    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view),
126                                  kPermissionsPadding);
127    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_view),
128                                   kPermissionsPadding);
129    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD);
130    GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
131    GtkTextTagTable* tag_table = gtk_text_buffer_get_tag_table(buffer);
132
133    GtkTextTag* padding_below_tag = gtk_text_tag_new(NULL);
134    g_object_set(G_OBJECT(padding_below_tag), "pixels-below-lines",
135                 kPermissionsPadding, NULL);
136    g_object_set(G_OBJECT(padding_below_tag), "pixels-below-lines-set",
137                 TRUE, NULL);
138    gtk_text_tag_table_add(tag_table, padding_below_tag);
139    g_object_unref(padding_below_tag);
140    GtkTextTag* padding_above_tag = gtk_text_tag_new(NULL);
141    g_object_set(G_OBJECT(padding_above_tag), "pixels-above-lines",
142                 kPermissionsPadding, NULL);
143    g_object_set(G_OBJECT(padding_above_tag), "pixels-above-lines-set",
144                 TRUE, NULL);
145    gtk_text_tag_table_add(tag_table, padding_above_tag);
146    g_object_unref(padding_above_tag);
147
148    GtkTextIter end_iter;
149    gtk_text_buffer_get_end_iter(buffer, &end_iter);
150    for (std::vector<string16>::const_iterator iter = permissions.begin();
151         iter != permissions.end(); ++iter) {
152      if (iter != permissions.begin())
153        gtk_text_buffer_insert(buffer, &end_iter, "\n", -1);
154      gtk_text_buffer_insert_with_tags(
155          buffer, &end_iter, UTF16ToUTF8(*iter).c_str(), -1,
156          padding_below_tag,
157          iter == permissions.begin() ? padding_above_tag : NULL,
158          NULL);
159    }
160  }
161
162  g_signal_connect(dialog, "response", G_CALLBACK(OnResponse), delegate);
163  gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
164
165  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
166  gtk_widget_show_all(dialog);
167  gtk_widget_grab_focus(close_button);
168}
169
170}  // namespace
171
172void ShowExtensionInstallDialog(
173    Profile* profile,
174    ExtensionInstallUI::Delegate* delegate,
175    const Extension* extension,
176    SkBitmap* icon,
177    const std::vector<string16>& permissions,
178    ExtensionInstallUI::PromptType type) {
179  Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
180  if (!browser) {
181    delegate->InstallUIAbort();
182    return;
183  }
184
185  BrowserWindowGtk* browser_window = static_cast<BrowserWindowGtk*>(
186      browser->window());
187  if (!browser_window) {
188    delegate->InstallUIAbort();
189    return;
190  }
191
192  ShowInstallDialog(
193      browser_window->window(), icon, extension, delegate, permissions, type);
194}
195