• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h"
6 
7 #include <string>
8 
9 #include "base/environment.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/shell_integration.h"
12 #include "chrome/browser/ui/gtk/gtk_util.h"
13 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
14 #include "chrome/browser/ui/web_applications/web_app_ui.h"
15 #include "chrome/common/extensions/extension.h"
16 #include "chrome/common/extensions/extension_resource.h"
17 #include "content/browser/browser_thread.h"
18 #include "content/browser/tab_contents/tab_contents.h"
19 #include "content/browser/tab_contents/tab_contents_delegate.h"
20 #include "grit/chromium_strings.h"
21 #include "grit/generated_resources.h"
22 #include "grit/locale_settings.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/gfx/gtk_util.h"
25 
26 namespace {
27 
28 // Size (in pixels) of the icon preview.
29 const int kIconPreviewSizePixels = 32;
30 
31 // Height (in lines) of the shortcut description label.
32 const int kDescriptionLabelHeightLines = 3;
33 
34 }  // namespace
35 
36 // static
Show(GtkWindow * parent,TabContentsWrapper * tab_contents)37 void CreateWebApplicationShortcutsDialogGtk::Show(
38     GtkWindow* parent, TabContentsWrapper* tab_contents) {
39   new CreateWebApplicationShortcutsDialogGtk(parent, tab_contents);
40 }
41 
Show(GtkWindow * parent,const Extension * app)42 void CreateChromeApplicationShortcutsDialogGtk::Show(GtkWindow* parent,
43                                                      const Extension* app) {
44   new CreateChromeApplicationShortcutsDialogGtk(parent, app);
45 }
46 
47 
CreateApplicationShortcutsDialogGtk(GtkWindow * parent)48 CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk(
49     GtkWindow* parent)
50   : parent_(parent),
51     desktop_checkbox_(NULL),
52     menu_checkbox_(NULL),
53     favicon_pixbuf_(NULL),
54     create_dialog_(NULL),
55     error_dialog_(NULL) {
56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57 
58   // Will be balanced by Release later.
59   AddRef();
60 }
61 
CreateIconPixBuf(const SkBitmap & bitmap)62 void CreateApplicationShortcutsDialogGtk::CreateIconPixBuf(
63     const SkBitmap& bitmap) {
64   // Prepare the icon. Try to scale it if it's too small, otherwise it would
65   // look weird.
66   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&shortcut_info_.favicon);
67   int pixbuf_width = gdk_pixbuf_get_width(pixbuf);
68   int pixbuf_height = gdk_pixbuf_get_height(pixbuf);
69   if (pixbuf_width == pixbuf_height && pixbuf_width < kIconPreviewSizePixels) {
70     // Only scale the pixbuf if it's a square (for simplicity).
71     // Generally it should be square, if it's a favicon or app icon.
72     // Use the highest quality interpolation. The scaling is
73     // going to have low quality anyway, because the initial image
74     // is likely small.
75     favicon_pixbuf_ = gdk_pixbuf_scale_simple(pixbuf,
76                                               kIconPreviewSizePixels,
77                                               kIconPreviewSizePixels,
78                                               GDK_INTERP_HYPER);
79     g_object_unref(pixbuf);
80   } else {
81     favicon_pixbuf_ = pixbuf;
82   }
83 }
84 
CreateDialogBox(GtkWindow * parent)85 void CreateApplicationShortcutsDialogGtk::CreateDialogBox(GtkWindow* parent) {
86   // Build the dialog.
87   create_dialog_ = gtk_dialog_new_with_buttons(
88       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_TITLE).c_str(),
89       parent,
90       (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
91       NULL);
92   gtk_widget_realize(create_dialog_);
93   gtk_window_set_resizable(GTK_WINDOW(create_dialog_), false);
94   gtk_util::AddButtonToDialog(create_dialog_,
95       l10n_util::GetStringUTF8(IDS_CANCEL).c_str(),
96       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
97   gtk_util::AddButtonToDialog(create_dialog_,
98       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_COMMIT).c_str(),
99       GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
100 
101   GtkWidget* content_area = GTK_DIALOG(create_dialog_)->vbox;
102   gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
103 
104   GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
105   gtk_container_add(GTK_CONTAINER(content_area), vbox);
106 
107   // Create a box containing basic information about the new shortcut: an image
108   // on the left, and a description on the right.
109   GtkWidget* hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
110   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
111   gtk_container_set_border_width(GTK_CONTAINER(hbox),
112                                  gtk_util::kControlSpacing);
113 
114   // Put the icon preview in place.
115   GtkWidget* favicon_image = gtk_image_new_from_pixbuf(favicon_pixbuf_);
116   gtk_box_pack_start(GTK_BOX(hbox), favicon_image, FALSE, FALSE, 0);
117 
118   // Create the label with application shortcut description.
119   GtkWidget* description_label = gtk_label_new(NULL);
120   gtk_box_pack_start(GTK_BOX(hbox), description_label, FALSE, FALSE, 0);
121   gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
122   gtk_widget_realize(description_label);
123 
124   // Set the size request on the label so it knows where to line wrap. The width
125   // is the desired size of the dialog less the space reserved for padding and
126   // the image.
127   int label_width;
128   gtk_util::GetWidgetSizeFromResources(
129       description_label,
130       IDS_CREATE_SHORTCUTS_DIALOG_WIDTH_CHARS, -1, &label_width, NULL);
131   label_width -= gtk_util::kControlSpacing * 3 +
132       gdk_pixbuf_get_width(favicon_pixbuf_);
133   gtk_util::SetLabelWidth(description_label, label_width);
134 
135   std::string description(UTF16ToUTF8(shortcut_info_.description));
136   std::string title(UTF16ToUTF8(shortcut_info_.title));
137   gtk_label_set_text(GTK_LABEL(description_label),
138                      (description.empty() ? title : description).c_str());
139 
140   // Label on top of the checkboxes.
141   GtkWidget* checkboxes_label = gtk_label_new(
142       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_LABEL).c_str());
143   gtk_misc_set_alignment(GTK_MISC(checkboxes_label), 0, 0);
144   gtk_box_pack_start(GTK_BOX(vbox), checkboxes_label, FALSE, FALSE, 0);
145 
146   // Desktop checkbox.
147   desktop_checkbox_ = gtk_check_button_new_with_label(
148       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX).c_str());
149   gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0);
150   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true);
151   g_signal_connect(desktop_checkbox_, "toggled",
152                    G_CALLBACK(OnToggleCheckboxThunk), this);
153 
154   // Menu checkbox.
155   menu_checkbox_ = gtk_check_button_new_with_label(
156       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str());
157   gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0);
158   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false);
159   g_signal_connect(menu_checkbox_, "toggled",
160                    G_CALLBACK(OnToggleCheckboxThunk), this);
161 
162   g_signal_connect(create_dialog_, "response",
163                    G_CALLBACK(OnCreateDialogResponseThunk), this);
164   gtk_widget_show_all(create_dialog_);
165 }
166 
~CreateApplicationShortcutsDialogGtk()167 CreateApplicationShortcutsDialogGtk::~CreateApplicationShortcutsDialogGtk() {
168   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169 
170   gtk_widget_destroy(create_dialog_);
171 
172   if (error_dialog_)
173     gtk_widget_destroy(error_dialog_);
174 
175   g_object_unref(favicon_pixbuf_);
176 }
177 
OnCreateDialogResponse(GtkWidget * widget,int response)178 void CreateApplicationShortcutsDialogGtk::OnCreateDialogResponse(
179     GtkWidget* widget, int response) {
180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
181 
182   if (response == GTK_RESPONSE_ACCEPT) {
183     shortcut_info_.create_on_desktop =
184         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_));
185     shortcut_info_.create_in_applications_menu =
186         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_));
187     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
188          NewRunnableMethod(this,
189              &CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut,
190              shortcut_info_));
191 
192     OnCreatedShortcut();
193   } else {
194     Release();
195   }
196 }
197 
OnErrorDialogResponse(GtkWidget * widget,int response)198 void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse(
199     GtkWidget* widget, int response) {
200   Release();
201 }
202 
CreateDesktopShortcut(const ShellIntegration::ShortcutInfo & shortcut_info)203 void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut(
204     const ShellIntegration::ShortcutInfo& shortcut_info) {
205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
206 
207   scoped_ptr<base::Environment> env(base::Environment::Create());
208 
209   std::string shortcut_template;
210   if (ShellIntegration::GetDesktopShortcutTemplate(env.get(),
211                                                    &shortcut_template)) {
212     ShellIntegration::CreateDesktopShortcut(shortcut_info,
213                                             shortcut_template);
214     Release();
215   } else {
216     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
217         NewRunnableMethod(this,
218             &CreateApplicationShortcutsDialogGtk::ShowErrorDialog));
219   }
220 }
221 
ShowErrorDialog()222 void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224 
225   // Hide the create dialog so that the user can no longer interact with it.
226   gtk_widget_hide(create_dialog_);
227 
228   error_dialog_ = gtk_dialog_new_with_buttons(
229       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(),
230       NULL,
231       (GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR),
232       GTK_STOCK_OK,
233       GTK_RESPONSE_ACCEPT,
234       NULL);
235   gtk_widget_realize(error_dialog_);
236   gtk_util::SetWindowSizeFromResources(
237       GTK_WINDOW(error_dialog_),
238       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS,
239       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES,
240       false);  // resizable
241   GtkWidget* content_area = GTK_DIALOG(error_dialog_)->vbox;
242   gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
243 
244   GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
245   gtk_container_add(GTK_CONTAINER(content_area), vbox);
246 
247   // Label on top of the checkboxes.
248   GtkWidget* description = gtk_label_new(
249       l10n_util::GetStringFUTF8(
250           IDS_CREATE_SHORTCUTS_ERROR_LABEL,
251           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str());
252   gtk_label_set_line_wrap(GTK_LABEL(description), TRUE);
253   gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
254   gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
255 
256   g_signal_connect(error_dialog_, "response",
257                    G_CALLBACK(OnErrorDialogResponseThunk), this);
258   gtk_widget_show_all(error_dialog_);
259 }
260 
OnToggleCheckbox(GtkWidget * sender)261 void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) {
262   gboolean can_accept = FALSE;
263 
264   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) ||
265       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
266     can_accept = TRUE;
267   }
268 
269   gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_),
270                                     GTK_RESPONSE_ACCEPT,
271                                     can_accept);
272 }
273 
CreateWebApplicationShortcutsDialogGtk(GtkWindow * parent,TabContentsWrapper * tab_contents)274 CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk(
275     GtkWindow* parent,
276     TabContentsWrapper* tab_contents)
277   : CreateApplicationShortcutsDialogGtk(parent),
278     tab_contents_(tab_contents) {
279 
280   // Get shortcut information now, it's needed for our UI.
281   web_app::GetShortcutInfoForTab(tab_contents_, &shortcut_info_);
282   CreateIconPixBuf(shortcut_info_.favicon);
283 
284   CreateDialogBox(parent);
285 }
286 
OnCreatedShortcut()287 void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() {
288   if (tab_contents_->tab_contents()->delegate())
289     tab_contents_->tab_contents()->delegate()->ConvertContentsToApplication(
290         tab_contents_->tab_contents());
291 }
292 
293 CreateChromeApplicationShortcutsDialogGtk::
CreateChromeApplicationShortcutsDialogGtk(GtkWindow * parent,const Extension * app)294     CreateChromeApplicationShortcutsDialogGtk(
295         GtkWindow* parent,
296         const Extension* app)
297       : CreateApplicationShortcutsDialogGtk(parent),
298         app_(app),
299         ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this))  {
300 
301   // Get shortcut information now, it's needed for our UI.
302   shortcut_info_.extension_id = app_->id();
303   shortcut_info_.url = GURL(app_->launch_web_url());
304   shortcut_info_.title = UTF8ToUTF16(app_->name());
305   shortcut_info_.description = UTF8ToUTF16(app_->description());
306 
307   // Get the icon.
308   const gfx::Size max_size(kIconPreviewSizePixels, kIconPreviewSizePixels);
309   ExtensionResource icon_resource = app_->GetIconResource(
310       kIconPreviewSizePixels, ExtensionIconSet::MATCH_BIGGER);
311 
312   // If no icon exists that is the desired size or larger, get the
313   // largest icon available:
314   if (icon_resource.empty())
315     icon_resource = app_->GetIconResource(
316         kIconPreviewSizePixels, ExtensionIconSet::MATCH_SMALLER);
317 
318   tracker_.LoadImage(app_,
319                      icon_resource,
320                      max_size,
321                      ImageLoadingTracker::DONT_CACHE);
322 }
323 
324 // Called by tracker_ when the app's icon is loaded.
OnImageLoaded(SkBitmap * image,const ExtensionResource & resource,int index)325 void CreateChromeApplicationShortcutsDialogGtk::OnImageLoaded(
326     SkBitmap* image, const ExtensionResource& resource, int index) {
327   if (image->isNull()) {
328     NOTREACHED() << "Corrupt image in profile?";
329     return;
330   }
331   shortcut_info_.favicon = *image;
332 
333   CreateIconPixBuf(*image);
334   CreateDialogBox(parent_);
335 }
336