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