- add sources.
[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/extension.h"
24 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_delegate.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(UTF16ToUTF8(shortcut_info_.description));
165   std::string title(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     creation_locations.in_applications_menu =
216         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_));
217     creation_locations.applications_menu_subdir = shortcut_menu_subdir_;
218     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
219         base::Bind(&CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut,
220                    this, shortcut_info_, creation_locations));
221
222     OnCreatedShortcut();
223   } else {
224     Release();
225   }
226 }
227
228 void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse(
229     GtkWidget* widget, int response) {
230   Release();
231 }
232
233 void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut(
234     const ShellIntegration::ShortcutInfo& shortcut_info,
235     const ShellIntegration::ShortcutLocations& creation_locations) {
236   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
237   ShellIntegrationLinux::CreateDesktopShortcut(shortcut_info,
238                                                creation_locations);
239   Release();
240 }
241
242 void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
243   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
244
245   // Hide the create dialog so that the user can no longer interact with it.
246   gtk_widget_hide(create_dialog_);
247
248   error_dialog_ = gtk_dialog_new_with_buttons(
249       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(),
250       NULL,
251       (GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR),
252       GTK_STOCK_OK,
253       GTK_RESPONSE_ACCEPT,
254       NULL);
255   gtk_widget_realize(error_dialog_);
256   gtk_util::SetWindowSizeFromResources(
257       GTK_WINDOW(error_dialog_),
258       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS,
259       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES,
260       false);  // resizable
261   GtkWidget* content_area =
262       gtk_dialog_get_content_area(GTK_DIALOG(error_dialog_));
263   gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
264
265   GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
266   gtk_container_add(GTK_CONTAINER(content_area), vbox);
267
268   // Label on top of the checkboxes.
269   GtkWidget* description = gtk_label_new(
270       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_LABEL).c_str());
271   gtk_label_set_line_wrap(GTK_LABEL(description), TRUE);
272   gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
273   gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
274
275   g_signal_connect(error_dialog_, "response",
276                    G_CALLBACK(OnErrorDialogResponseThunk), this);
277   gtk_widget_show_all(error_dialog_);
278 }
279
280 void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) {
281   gboolean can_accept = FALSE;
282
283   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) ||
284       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
285     can_accept = TRUE;
286   }
287
288   gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_),
289                                     GTK_RESPONSE_ACCEPT,
290                                     can_accept);
291 }
292
293 CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk(
294     GtkWindow* parent,
295     content::WebContents* web_contents)
296   : CreateApplicationShortcutsDialogGtk(parent),
297     web_contents_(web_contents) {
298
299   // Get shortcut information now, it's needed for our UI.
300   web_app::GetShortcutInfoForTab(web_contents, &shortcut_info_);
301   CreateIconPixBuf(shortcut_info_.favicon);
302
303   // NOTE: Leave shortcut_menu_subdir_ blank to create URL app shortcuts in the
304   // top-level menu.
305
306   CreateDialogBox(parent);
307 }
308
309 void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() {
310   Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
311   if (browser)
312     chrome::ConvertTabToAppWindow(browser, web_contents_);
313 }
314
315 CreateChromeApplicationShortcutsDialogGtk::
316     CreateChromeApplicationShortcutsDialogGtk(
317         GtkWindow* parent,
318         Profile* profile,
319         const Extension* app,
320         const base::Closure& close_callback)
321     : CreateApplicationShortcutsDialogGtk(parent),
322       app_(app),
323       profile_path_(profile->GetPath()),
324       close_callback_(close_callback) {
325
326   // Place Chrome app shortcuts in the "Chrome Apps" submenu.
327   shortcut_menu_subdir_ = web_app::GetAppShortcutsSubdirName();
328
329   // Get shortcut information and icon now; they are needed for our UI.
330   web_app::UpdateShortcutInfoAndIconForApp(
331       *app, profile,
332       base::Bind(
333           &CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded,
334           this));
335 }
336
337 CreateChromeApplicationShortcutsDialogGtk::
338     ~CreateChromeApplicationShortcutsDialogGtk() {
339   if (!close_callback_.is_null())
340     close_callback_.Run();
341 }
342
343 // Called when the app's ShortcutInfo (with icon) is loaded.
344 void CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded(
345   const ShellIntegration::ShortcutInfo& shortcut_info) {
346   shortcut_info_ = shortcut_info;
347
348   CreateIconPixBuf(shortcut_info_.favicon);
349   CreateDialogBox(parent_);
350 }
351
352 void CreateChromeApplicationShortcutsDialogGtk::CreateDesktopShortcut(
353     const ShellIntegration::ShortcutInfo& shortcut_info,
354     const ShellIntegration::ShortcutLocations& creation_locations) {
355   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
356
357   if (web_app::CreateShortcutsOnFileThread(
358           shortcut_info, creation_locations,
359           web_app::SHORTCUT_CREATION_BY_USER)) {
360     Release();
361   } else {
362     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
363         base::Bind(&CreateChromeApplicationShortcutsDialogGtk::ShowErrorDialog,
364                    this));
365   }
366 }