Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / create_application_shortcuts_dialog_gtk.cc
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/create_application_shortcuts_dialog_gtk.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/environment.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/shell_integration.h"
14 #include "chrome/browser/shell_integration_linux.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_commands.h"
17 #include "chrome/browser/ui/browser_dialogs.h"
18 #include "chrome/browser/ui/browser_finder.h"
19 #include "chrome/browser/ui/gtk/gtk_util.h"
20 #include "chrome/browser/ui/web_applications/web_app_ui.h"
21 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
22 #include "chrome/browser/web_applications/web_app.h"
23 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "extensions/common/extension.h"
28 #include "grit/chromium_strings.h"
29 #include "grit/generated_resources.h"
30 #include "grit/locale_settings.h"
31 #include "grit/theme_resources.h"
32 #include "ui/base/gtk/gtk_hig_constants.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/gfx/gtk_util.h"
35 #include "ui/gfx/image/image.h"
36 #include "ui/gfx/image/image_family.h"
37 #include "ui/gfx/image/image_skia.h"
38
39 using content::BrowserThread;
40 using extensions::Extension;
41
42 namespace {
43
44 // Size (in pixels) of the icon preview.
45 const int kIconPreviewSizePixels = 32;
46
47 // Minimum width (in pixels) of the shortcut description label.
48 const int kDescriptionLabelMinimumWidthPixels = 200;
49
50 }  // namespace
51
52 namespace chrome {
53
54 void ShowCreateWebAppShortcutsDialog(gfx::NativeWindow parent_window,
55                                      content::WebContents* web_contents) {
56   new CreateWebApplicationShortcutsDialogGtk(parent_window, web_contents);
57 }
58
59 void ShowCreateChromeAppShortcutsDialog(
60     gfx::NativeWindow parent_window,
61     Profile* profile,
62     const extensions::Extension* app,
63     const base::Closure& close_callback) {
64   new CreateChromeApplicationShortcutsDialogGtk(parent_window, profile, app,
65                                                 close_callback);
66 }
67
68 }  // namespace chrome
69
70 CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk(
71     GtkWindow* parent)
72   : parent_(parent),
73     desktop_checkbox_(NULL),
74     menu_checkbox_(NULL),
75     favicon_pixbuf_(NULL),
76     create_dialog_(NULL),
77     error_dialog_(NULL) {
78   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79
80   // Will be balanced by Release later.
81   AddRef();
82 }
83
84 void CreateApplicationShortcutsDialogGtk::CreateIconPixBuf(
85     const gfx::ImageFamily& image) {
86   // Get the icon closest to the desired preview size.
87   const gfx::Image* icon = image.GetBest(kIconPreviewSizePixels,
88                                          kIconPreviewSizePixels);
89   // There must be at least one icon in the image family.
90   CHECK(icon);
91   GdkPixbuf* pixbuf = icon->CopyGdkPixbuf();
92   // Prepare the icon. Scale it to the correct size to display in the dialog.
93   int pixbuf_width = gdk_pixbuf_get_width(pixbuf);
94   int pixbuf_height = gdk_pixbuf_get_height(pixbuf);
95   if (pixbuf_width == pixbuf_height) {
96     // Only scale the pixbuf if it's a square (for simplicity).
97     // Generally it should be square, if it's a favicon or app icon.
98     // Use the highest quality interpolation.
99     favicon_pixbuf_ = gdk_pixbuf_scale_simple(pixbuf,
100                                               kIconPreviewSizePixels,
101                                               kIconPreviewSizePixels,
102                                               GDK_INTERP_HYPER);
103     g_object_unref(pixbuf);
104   } else {
105     favicon_pixbuf_ = pixbuf;
106   }
107 }
108
109 void CreateApplicationShortcutsDialogGtk::CreateDialogBox(GtkWindow* parent) {
110   // Build the dialog.
111   create_dialog_ = gtk_dialog_new_with_buttons(
112       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_TITLE).c_str(),
113       parent,
114       (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
115       NULL);
116   gtk_widget_realize(create_dialog_);
117   gtk_window_set_resizable(GTK_WINDOW(create_dialog_), false);
118   gtk_util::AddButtonToDialog(create_dialog_,
119       l10n_util::GetStringUTF8(IDS_CANCEL).c_str(),
120       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
121   gtk_util::AddButtonToDialog(create_dialog_,
122       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_COMMIT).c_str(),
123       GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
124
125   GtkWidget* content_area =
126       gtk_dialog_get_content_area(GTK_DIALOG(create_dialog_));
127   gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
128
129   GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
130   gtk_container_add(GTK_CONTAINER(content_area), vbox);
131
132   // Create a box containing basic information about the new shortcut: an image
133   // on the left, and a description on the right.
134   GtkWidget* hbox = gtk_hbox_new(FALSE, ui::kControlSpacing);
135   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
136   gtk_container_set_border_width(GTK_CONTAINER(hbox),
137                                  ui::kControlSpacing);
138
139   // Put the icon preview in place.
140   GtkWidget* favicon_image = gtk_image_new_from_pixbuf(favicon_pixbuf_);
141   gtk_box_pack_start(GTK_BOX(hbox), favicon_image, FALSE, FALSE, 0);
142
143   // Create the label with application shortcut description.
144   GtkWidget* description_label = gtk_label_new(NULL);
145   gtk_box_pack_start(GTK_BOX(hbox), description_label, FALSE, FALSE, 0);
146   gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
147   gtk_widget_realize(description_label);
148
149   // Set the size request on the label so it knows where to line wrap. The width
150   // is the desired size of the dialog less the space reserved for padding and
151   // the image.
152   int label_width;
153   gtk_util::GetWidgetSizeFromResources(
154       description_label,
155       IDS_CREATE_SHORTCUTS_DIALOG_WIDTH_CHARS, -1, &label_width, NULL);
156   label_width -= ui::kControlSpacing * 3 +
157       gdk_pixbuf_get_width(favicon_pixbuf_);
158   // Enforce a minimum width, so that very large icons do not cause the label
159   // width to shrink to unreadable size, or become negative (which would crash).
160   if (label_width < kDescriptionLabelMinimumWidthPixels)
161     label_width = kDescriptionLabelMinimumWidthPixels;
162   gtk_util::SetLabelWidth(description_label, label_width);
163
164   std::string description(base::UTF16ToUTF8(shortcut_info_.description));
165   std::string title(base::UTF16ToUTF8(shortcut_info_.title));
166   gtk_label_set_text(GTK_LABEL(description_label),
167                      (description.empty() ? title : description).c_str());
168
169   // Label on top of the checkboxes.
170   GtkWidget* checkboxes_label = gtk_label_new(
171       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_LABEL).c_str());
172   gtk_misc_set_alignment(GTK_MISC(checkboxes_label), 0, 0);
173   gtk_box_pack_start(GTK_BOX(vbox), checkboxes_label, FALSE, FALSE, 0);
174
175   // Desktop checkbox.
176   desktop_checkbox_ = gtk_check_button_new_with_label(
177       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX).c_str());
178   gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0);
179   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true);
180   g_signal_connect(desktop_checkbox_, "toggled",
181                    G_CALLBACK(OnToggleCheckboxThunk), this);
182
183   // Menu checkbox.
184   menu_checkbox_ = gtk_check_button_new_with_label(
185       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str());
186   gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0);
187   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false);
188   g_signal_connect(menu_checkbox_, "toggled",
189                    G_CALLBACK(OnToggleCheckboxThunk), this);
190
191   g_signal_connect(create_dialog_, "response",
192                    G_CALLBACK(OnCreateDialogResponseThunk), this);
193   gtk_widget_show_all(create_dialog_);
194 }
195
196 CreateApplicationShortcutsDialogGtk::~CreateApplicationShortcutsDialogGtk() {
197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198
199   gtk_widget_destroy(create_dialog_);
200
201   if (error_dialog_)
202     gtk_widget_destroy(error_dialog_);
203
204   g_object_unref(favicon_pixbuf_);
205 }
206
207 void CreateApplicationShortcutsDialogGtk::OnCreateDialogResponse(
208     GtkWidget* widget, int response) {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210
211   if (response == GTK_RESPONSE_ACCEPT) {
212     ShellIntegration::ShortcutLocations creation_locations;
213     creation_locations.on_desktop =
214         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_));
215     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
216       creation_locations.applications_menu_location =
217           create_in_chrome_apps_subdir_ ?
218               ShellIntegration::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS :
219               ShellIntegration::APP_MENU_LOCATION_ROOT;
220     }
221     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
222         base::Bind(&CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut,
223                    this, shortcut_info_, creation_locations));
224
225     OnCreatedShortcut();
226   } else {
227     Release();
228   }
229 }
230
231 void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse(
232     GtkWidget* widget, int response) {
233   Release();
234 }
235
236 void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut(
237     const ShellIntegration::ShortcutInfo& shortcut_info,
238     const ShellIntegration::ShortcutLocations& creation_locations) {
239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
240   ShellIntegrationLinux::CreateDesktopShortcut(shortcut_info,
241                                                creation_locations);
242   Release();
243 }
244
245 void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247
248   // Hide the create dialog so that the user can no longer interact with it.
249   gtk_widget_hide(create_dialog_);
250
251   error_dialog_ = gtk_dialog_new_with_buttons(
252       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(),
253       NULL,
254       (GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR),
255       GTK_STOCK_OK,
256       GTK_RESPONSE_ACCEPT,
257       NULL);
258   gtk_widget_realize(error_dialog_);
259   gtk_util::SetWindowSizeFromResources(
260       GTK_WINDOW(error_dialog_),
261       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS,
262       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES,
263       false);  // resizable
264   GtkWidget* content_area =
265       gtk_dialog_get_content_area(GTK_DIALOG(error_dialog_));
266   gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
267
268   GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
269   gtk_container_add(GTK_CONTAINER(content_area), vbox);
270
271   // Label on top of the checkboxes.
272   GtkWidget* description = gtk_label_new(
273       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_LABEL).c_str());
274   gtk_label_set_line_wrap(GTK_LABEL(description), TRUE);
275   gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
276   gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
277
278   g_signal_connect(error_dialog_, "response",
279                    G_CALLBACK(OnErrorDialogResponseThunk), this);
280   gtk_widget_show_all(error_dialog_);
281 }
282
283 void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) {
284   gboolean can_accept = FALSE;
285
286   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) ||
287       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
288     can_accept = TRUE;
289   }
290
291   gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_),
292                                     GTK_RESPONSE_ACCEPT,
293                                     can_accept);
294 }
295
296 CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk(
297     GtkWindow* parent,
298     content::WebContents* web_contents)
299   : CreateApplicationShortcutsDialogGtk(parent),
300     web_contents_(web_contents) {
301
302   // Get shortcut information now, it's needed for our UI.
303   web_app::GetShortcutInfoForTab(web_contents, &shortcut_info_);
304   CreateIconPixBuf(shortcut_info_.favicon);
305
306   // Create URL app shortcuts in the top-level menu.
307   create_in_chrome_apps_subdir_ = false;
308
309   CreateDialogBox(parent);
310 }
311
312 void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() {
313   Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
314   if (browser)
315     chrome::ConvertTabToAppWindow(browser, web_contents_);
316 }
317
318 CreateChromeApplicationShortcutsDialogGtk::
319     CreateChromeApplicationShortcutsDialogGtk(
320         GtkWindow* parent,
321         Profile* profile,
322         const Extension* app,
323         const base::Closure& close_callback)
324     : CreateApplicationShortcutsDialogGtk(parent),
325       app_(app),
326       profile_path_(profile->GetPath()),
327       close_callback_(close_callback) {
328
329   // Place Chrome app shortcuts in the "Chrome Apps" submenu.
330   create_in_chrome_apps_subdir_ = true;
331
332   // Get shortcut information and icon now; they are needed for our UI.
333   web_app::UpdateShortcutInfoAndIconForApp(
334       app, profile,
335       base::Bind(
336           &CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded,
337           this));
338 }
339
340 CreateChromeApplicationShortcutsDialogGtk::
341     ~CreateChromeApplicationShortcutsDialogGtk() {
342   if (!close_callback_.is_null())
343     close_callback_.Run();
344 }
345
346 // Called when the app's ShortcutInfo (with icon) is loaded.
347 void CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded(
348   const ShellIntegration::ShortcutInfo& shortcut_info) {
349   shortcut_info_ = shortcut_info;
350
351   CreateIconPixBuf(shortcut_info_.favicon);
352   CreateDialogBox(parent_);
353 }
354
355 void CreateChromeApplicationShortcutsDialogGtk::CreateDesktopShortcut(
356     const ShellIntegration::ShortcutInfo& shortcut_info,
357     const ShellIntegration::ShortcutLocations& creation_locations) {
358   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
359
360   if (web_app::CreateShortcutsOnFileThread(
361           shortcut_info, creation_locations,
362           web_app::SHORTCUT_CREATION_BY_USER)) {
363     Release();
364   } else {
365     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
366         base::Bind(&CreateChromeApplicationShortcutsDialogGtk::ShowErrorDialog,
367                    this));
368   }
369 }